Merge pull request #8650 from turbo124/v5-stable

v5.6.19
This commit is contained in:
David Bomba 2023-07-16 12:46:04 +10:00 committed by GitHub
commit 7f9b784b20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 148394 additions and 146823 deletions

View File

@ -1 +1 @@
5.6.18 5.6.19

View File

@ -17,7 +17,7 @@ class EncryptedCast implements CastsAttributes
{ {
public function get($model, string $key, $value, array $attributes) public function get($model, string $key, $value, array $attributes)
{ {
return strlen($value) > 1 ? decrypt($value) : null; return is_string($value) && strlen($value) > 1 ? decrypt($value) : null;
} }
public function set($model, string $key, $value, array $attributes) public function set($model, string $key, $value, array $attributes)

View File

@ -13,6 +13,7 @@ namespace App\Export\CSV;
use App\Utils\Number; use App\Utils\Number;
use App\Models\Client; use App\Models\Client;
use App\Models\Expense;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\GatewayType; use App\Models\GatewayType;
use App\Models\Payment; use App\Models\Payment;
@ -42,9 +43,41 @@ class BaseExport
public array $forced_keys = []; public array $forced_keys = [];
protected array $vendor_report_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',
'client_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',
];
protected array $client_report_keys = [ protected array $client_report_keys = [
"name" => "client.name", "name" => "client.name",
"user" => "client.user_id", "user" => "client.user",
"assigned_user" => "client.assigned_user",
"balance" => "client.balance", "balance" => "client.balance",
"paid_to_date" => "client.paid_to_date", "paid_to_date" => "client.paid_to_date",
"currency" => "client.currency_id", "currency" => "client.currency_id",
@ -103,6 +136,42 @@ class BaseExport
"user" => "invoice.user_id", "user" => "invoice.user_id",
]; ];
protected array $purchase_order_report_keys = [
'amount' => 'purchase_order.amount',
'balance' => 'purchase_order.balance',
'vendor' => 'purchase_order.vendor_id',
// 'custom_surcharge1' => 'purchase_order.custom_surcharge1',
// 'custom_surcharge2' => 'purchase_order.custom_surcharge2',
// 'custom_surcharge3' => 'purchase_order.custom_surcharge3',
// 'custom_surcharge4' => 'purchase_order.custom_surcharge4',
'custom_value1' => 'purchase_order.custom_value1',
'custom_value2' => 'purchase_order.custom_value2',
'custom_value3' => 'purchase_order.custom_value3',
'custom_value4' => 'purchase_order.custom_value4',
'date' => 'purchase_order.date',
'discount' => 'purchase_order.discount',
'due_date' => 'purchase_order.due_date',
'exchange_rate' => 'purchase_order.exchange_rate',
'footer' => 'purchase_order.footer',
'number' => 'purchase_order.number',
'paid_to_date' => 'purchase_order.paid_to_date',
'partial' => 'purchase_order.partial',
'partial_due_date' => 'purchase_order.partial_due_date',
'po_number' => 'purchase_order.po_number',
'private_notes' => 'purchase_order.private_notes',
'public_notes' => 'purchase_order.public_notes',
'status' => 'purchase_order.status_id',
'tax_name1' => 'purchase_order.tax_name1',
'tax_name2' => 'purchase_order.tax_name2',
'tax_name3' => 'purchase_order.tax_name3',
'tax_rate1' => 'purchase_order.tax_rate1',
'tax_rate2' => 'purchase_order.tax_rate2',
'tax_rate3' => 'purchase_order.tax_rate3',
'terms' => 'purchase_order.terms',
'total_taxes' => 'purchase_order.total_taxes',
'currency_id' => 'purchase_order.currency_id',
];
protected array $item_report_keys = [ protected array $item_report_keys = [
"quantity" => "item.quantity", "quantity" => "item.quantity",
"cost" => "item.cost", "cost" => "item.cost",
@ -197,6 +266,58 @@ class BaseExport
"assigned_user" => "payment.assigned_user_id", "assigned_user" => "payment.assigned_user_id",
]; ];
protected array $expense_report_keys = [
'amount' => 'expense.amount',
'category' => 'expense.category_id',
'client' => 'expense.client_id',
'custom_value1' => 'expense.custom_value1',
'custom_value2' => 'expense.custom_value2',
'custom_value3' => 'expense.custom_value3',
'custom_value4' => 'expense.custom_value4',
'currency' => 'expense.currency_id',
'date' => 'expense.date',
'exchange_rate' => 'expense.exchange_rate',
'converted_amount' => 'expense.foreign_amount',
'invoice_currency_id' => 'expense.invoice_currency_id',
'payment_date' => 'expense.payment_date',
'number' => 'expense.number',
'payment_type_id' => 'expense.payment_type_id',
'private_notes' => 'expense.private_notes',
'project' => 'expense.project_id',
'public_notes' => 'expense.public_notes',
'tax_amount1' => 'expense.tax_amount1',
'tax_amount2' => 'expense.tax_amount2',
'tax_amount3' => 'expense.tax_amount3',
'tax_name1' => 'expense.tax_name1',
'tax_name2' => 'expense.tax_name2',
'tax_name3' => 'expense.tax_name3',
'tax_rate1' => 'expense.tax_rate1',
'tax_rate2' => 'expense.tax_rate2',
'tax_rate3' => 'expense.tax_rate3',
'transaction_reference' => 'expense.transaction_reference',
'vendor' => 'expense.vendor_id',
'invoice' => 'expense.invoice_id',
'user' => 'expense.user',
'assigned_user' => 'expense.assigned_user',
];
protected array $task_report_keys = [
'start_date' => 'task.start_date',
'end_date' => 'task.end_date',
'duration' => 'task.duration',
'rate' => 'task.rate',
'number' => 'task.number',
'description' => 'task.description',
'custom_value1' => 'task.custom_value1',
'custom_value2' => 'task.custom_value2',
'custom_value3' => 'task.custom_value3',
'custom_value4' => 'task.custom_value4',
'status' => 'task.status_id',
'project' => 'task.project_id',
'invoice' => 'task.invoice_id',
'client' => 'task.client_id',
];
protected function filterByClients($query) protected function filterByClients($query)
{ {
if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') { if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') {
@ -222,7 +343,11 @@ class BaseExport
match($parts[0]) { match($parts[0]) {
'contact' => $value = $this->resolveClientContactKey($parts[1], $entity, $transformer), 'contact' => $value = $this->resolveClientContactKey($parts[1], $entity, $transformer),
'client' => $value = $this->resolveClientKey($parts[1], $entity, $transformer), 'client' => $value = $this->resolveClientKey($parts[1], $entity, $transformer),
'expense' => $value = $this->resolveExpenseKey($parts[1], $entity, $transformer),
'vendor' => $value = $this->resolveVendorKey($parts[1], $entity, $transformer),
'vendor_contact' => $value = $this->resolveVendorContactKey($parts[1], $entity, $transformer),
'invoice' => $value = $this->resolveInvoiceKey($parts[1], $entity, $transformer), 'invoice' => $value = $this->resolveInvoiceKey($parts[1], $entity, $transformer),
'purchase_order' => $value = $this->resolvePurchaseOrderKey($parts[1], $entity, $transformer),
'payment' => $value = $this->resolvePaymentKey($parts[1], $entity, $transformer), 'payment' => $value = $this->resolvePaymentKey($parts[1], $entity, $transformer),
default => $value = '' default => $value = ''
}; };
@ -233,14 +358,106 @@ class BaseExport
private function resolveClientContactKey($column, $entity, $transformer) private function resolveClientContactKey($column, $entity, $transformer)
{ {
if(!$entity->client) {
return "";
}
$primary_contact = $entity->client->primary_contact()->first() ?? $entity->client->contacts()->first(); $primary_contact = $entity->client->primary_contact()->first() ?? $entity->client->contacts()->first();
return $primary_contact?->{$column} ?? ''; return $primary_contact ? $primary_contact?->{$column} ?? '' : '';
} }
private function resolveVendorContactKey($column, $entity, $transformer)
{
if(!$entity->vendor)
return "";
$primary_contact = $entity->vendor->primary_contact()->first() ?? $entity->vendor->contacts()->first();
return $primary_contact ? $primary_contact?->{$column} ?? '' : '';
}
private function resolveExpenseKey($column, $entity, $transformer)
{
if($column == 'user' && $entity?->expense?->user)
return $entity->expense->user->present()->name() ?? ' ';
if($column == 'assigned_user' && $entity?->expense?->assigned_user)
return $entity->expense->assigned_user->present()->name() ?? ' ';
if($column == 'category' && $entity->expense) {
return $entity->expense->category?->name ?? ' ';
}
if($entity instanceof Expense)
return '';
$transformed_entity = $transformer->includeExpense($entity);
$manager = new Manager();
$manager->setSerializer(new ArraySerializer());
$transformed_entity = $manager->createData($transformed_entity)->toArray();
if(array_key_exists($column, $transformed_entity))
return $transformed_entity[$column];
if(property_exists($entity, $column))
return $entity?->{$column} ?? '';
nlog("export: Could not resolve expense key: {$column}");
return '';
}
private function resolveVendorKey($column, $entity, $transformer)
{
if(!$entity->vendor)
return '';
$transformed_entity = $transformer->includeVendor($entity);
$manager = new Manager();
$manager->setSerializer(new ArraySerializer());
$transformed_entity = $manager->createData($transformed_entity)->toArray();
if($column == 'name')
return $entity->vendor->present()->name() ?: '';
if($column == 'user_id')
return $entity->vendor->user->present()->name() ?: '';
if($column == 'country_id')
return $entity->vendor->country ? ctrans("texts.country_{$entity->vendor->country->name}") : '';
if ($column == 'currency_id') {
return $entity->vendor->currency() ? $entity->vendor->currency()->code : $entity->company->currency()->code;
}
if($column == 'status')
return $entity->stringStatus($entity->status_id) ?: '';
if(array_key_exists($column, $transformed_entity))
return $transformed_entity[$column];
nlog("export: Could not resolve vendor key: {$column}");
return '';
}
private function resolveClientKey($column, $entity, $transformer) private function resolveClientKey($column, $entity, $transformer)
{ {
if(!$entity->client)
return '';
$transformed_client = $transformer->includeClient($entity); $transformed_client = $transformer->includeClient($entity);
$manager = new Manager(); $manager = new Manager();
@ -282,6 +499,18 @@ class BaseExport
} }
private function resolvePurchaseOrderKey($column, $entity, $transformer)
{
nlog("searching for {$column}");
$transformed_entity = $transformer->transform($entity);
if($column == 'status')
return $entity->stringStatus($entity->status_id);
return '';
}
private function resolveInvoiceKey($column, $entity, $transformer) private function resolveInvoiceKey($column, $entity, $transformer)
{ {
nlog("searching for {$column}"); nlog("searching for {$column}");
@ -496,6 +725,8 @@ class BaseExport
{ {
$header = []; $header = [];
// nlog($this->input['report_keys']);
foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) { foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) {
$key = array_search($value, $this->entity_keys); $key = array_search($value, $this->entity_keys);
@ -503,48 +734,75 @@ class BaseExport
$prefix = ''; $prefix = '';
if(!$key) { if(!$key) {
$prefix = stripos($value, 'client.') !== false ? ctrans('texts.client') : ctrans('texts.contact'); $prefix = stripos($value, 'client.') !== false ? ctrans('texts.client')." " : ctrans('texts.contact')." ";
$key = array_search($value, $this->client_report_keys); $key = array_search($value, $this->client_report_keys);
} }
if(!$key) { if(!$key) {
$prefix = ctrans('texts.invoice'); $prefix = ctrans('texts.invoice')." ";
$key = array_search($value, $this->invoice_report_keys); $key = array_search($value, $this->invoice_report_keys);
} }
if(!$key) { if(!$key) {
$prefix = ctrans('texts.payment'); $prefix = ctrans('texts.payment')." ";
$key = array_search($value, $this->payment_report_keys); $key = array_search($value, $this->payment_report_keys);
} }
if(!$key) { if(!$key) {
$prefix = ctrans('texts.quote'); $prefix = ctrans('texts.quote')." ";
$key = array_search($value, $this->quote_report_keys); $key = array_search($value, $this->quote_report_keys);
} }
if(!$key) { if(!$key) {
$prefix = ctrans('texts.credit'); $prefix = ctrans('texts.credit')." ";
$key = array_search($value, $this->credit_report_keys); $key = array_search($value, $this->credit_report_keys);
} }
if(!$key) { if(!$key) {
$prefix = ctrans('texts.item'); $prefix = ctrans('texts.item')." ";
$key = array_search($value, $this->item_report_keys); $key = array_search($value, $this->item_report_keys);
} }
if(!$key) {
$prefix = ctrans('texts.expense')." ";
$key = array_search($value, $this->expense_report_keys);
}
if(!$key) {
$prefix = ctrans('texts.task')." ";
$key = array_search($value, $this->task_report_keys);
}
if(!$key) {
$prefix = ctrans('texts.vendor')." ";
$key = array_search($value, $this->vendor_report_keys);
}
if(!$key) {
$prefix = ctrans('texts.purchase_order')." ";
$key = array_search($value, $this->purchase_order_report_keys);
}
if(!$key) { if(!$key) {
$prefix = ''; $prefix = '';
} }
$key = str_replace('item.', '', $key); $key = str_replace('item.', '', $key);
$key = str_replace('recurring_invoice.', '', $key);
$key = str_replace('invoice.', '', $key); $key = str_replace('invoice.', '', $key);
$key = str_replace('quote.', '', $key);
$key = str_replace('credit.', '', $key);
$key = str_replace('task.', '', $key);
$key = str_replace('client.', '', $key); $key = str_replace('client.', '', $key);
$key = str_replace('vendor.', '', $key);
$key = str_replace('contact.', '', $key); $key = str_replace('contact.', '', $key);
$key = str_replace('payment.', '', $key); $key = str_replace('payment.', '', $key);
$key = str_replace('expense.', '', $key);
$header[] = "{$prefix} " . ctrans("texts.{$key}"); $header[] = "{$prefix}" . ctrans("texts.{$key}");
} }
// nlog($header);
return $header; return $header;
} }

View File

@ -82,7 +82,6 @@ class ClientExport extends BaseExport
]; ];
public array $forced_keys = [ public array $forced_keys = [
'status',
]; ];
public function __construct(Company $company, array $input) public function __construct(Company $company, array $input)
@ -132,6 +131,8 @@ class ClientExport extends BaseExport
$transformed_client = $this->client_transformer->transform($client); $transformed_client = $this->client_transformer->transform($client);
$transformed_contact = [];
if ($contact = $client->contacts()->first()) { if ($contact = $client->contacts()->first()) {
$transformed_contact = $this->contact_transformer->transform($contact); $transformed_contact = $this->contact_transformer->transform($contact);
} }
@ -141,14 +142,12 @@ class ClientExport extends BaseExport
foreach (array_values($this->input['report_keys']) as $key) { foreach (array_values($this->input['report_keys']) as $key) {
$parts = explode('.', $key); $parts = explode('.', $key);
$keyval = array_search($key, $this->entity_keys);
if (is_array($parts) && $parts[0] == 'client' && array_key_exists($parts[1], $transformed_client)) { if (is_array($parts) && $parts[0] == 'client' && array_key_exists($parts[1], $transformed_client)) {
$entity[$keyval] = $transformed_client[$parts[1]]; $entity[$key] = $transformed_client[$parts[1]];
} elseif (is_array($parts) && $parts[0] == 'contact' && array_key_exists($parts[1], $transformed_contact)) { } elseif (is_array($parts) && $parts[0] == 'contact' && array_key_exists($parts[1], $transformed_contact)) {
$entity[$keyval] = $transformed_contact[$parts[1]]; $entity[$key] = $transformed_contact[$parts[1]];
} else { } else {
$entity[$keyval] = ''; $entity[$key] = '';
} }
} }
@ -157,6 +156,14 @@ class ClientExport extends BaseExport
private function decorateAdvancedFields(Client $client, array $entity) :array private function decorateAdvancedFields(Client $client, array $entity) :array
{ {
if (in_array('client.user', $this->input['report_keys'])) {
$entity['client.user'] = $client->user->present()->name();
}
if (in_array('client.assigned_user', $this->input['report_keys'])) {
$entity['client.assigned_user'] = $client->assigned_user ? $client->user->present()->name() : '';
}
if (in_array('client.country_id', $this->input['report_keys'])) { if (in_array('client.country_id', $this->input['report_keys'])) {
$entity['country'] = $client->country ? ctrans("texts.country_{$client->country->name}") : ''; $entity['country'] = $client->country ? ctrans("texts.country_{$client->country->name}") : '';
} }
@ -173,8 +180,6 @@ class ClientExport extends BaseExport
$entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : ''; $entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : '';
} }
$entity['status'] = $this->calculateStatus($client);
return $entity; return $entity;
} }
@ -185,7 +190,7 @@ class ClientExport extends BaseExport
} }
if ($client->deleted_at) { if ($client->deleted_at) {
return ctrans('texts.arcvived'); return ctrans('texts.archived');
} }
return ctrans('texts.active'); return ctrans('texts.active');

View File

@ -30,36 +30,38 @@ class ExpenseExport extends BaseExport
public Writer $csv; public Writer $csv;
public array $entity_keys = [ public array $entity_keys = [
'amount' => 'amount', 'amount' => 'expense.amount',
'category' => 'category_id', 'category' => 'expense.category',
'client' => 'client_id', 'client' => 'expense.client_id',
'custom_value1' => 'custom_value1', 'custom_value1' => 'expense.custom_value1',
'custom_value2' => 'custom_value2', 'custom_value2' => 'expense.custom_value2',
'custom_value3' => 'custom_value3', 'custom_value3' => 'expense.custom_value3',
'custom_value4' => 'custom_value4', 'custom_value4' => 'expense.custom_value4',
'currency' => 'currency_id', 'currency' => 'expense.currency_id',
'date' => 'date', 'date' => 'expense.date',
'exchange_rate' => 'exchange_rate', 'exchange_rate' => 'expense.exchange_rate',
'converted_amount' => 'foreign_amount', 'converted_amount' => 'expense.foreign_amount',
'invoice_currency_id' => 'invoice_currency_id', 'invoice_currency_id' => 'expense.invoice_currency_id',
'payment_date' => 'payment_date', 'payment_date' => 'expense.payment_date',
'number' => 'number', 'number' => 'expense.number',
'payment_type_id' => 'payment_type_id', 'payment_type_id' => 'expense.payment_type_id',
'private_notes' => 'private_notes', 'private_notes' => 'expense.private_notes',
'project' => 'project_id', 'project' => 'expense.project_id',
'public_notes' => 'public_notes', 'public_notes' => 'expense.public_notes',
'tax_amount1' => 'tax_amount1', 'tax_amount1' => 'expense.tax_amount1',
'tax_amount2' => 'tax_amount2', 'tax_amount2' => 'expense.tax_amount2',
'tax_amount3' => 'tax_amount3', 'tax_amount3' => 'expense.tax_amount3',
'tax_name1' => 'tax_name1', 'tax_name1' => 'expense.tax_name1',
'tax_name2' => 'tax_name2', 'tax_name2' => 'expense.tax_name2',
'tax_name3' => 'tax_name3', 'tax_name3' => 'expense.tax_name3',
'tax_rate1' => 'tax_rate1', 'tax_rate1' => 'expense.tax_rate1',
'tax_rate2' => 'tax_rate2', 'tax_rate2' => 'expense.tax_rate2',
'tax_rate3' => 'tax_rate3', 'tax_rate3' => 'expense.tax_rate3',
'transaction_reference' => 'transaction_reference', 'transaction_reference' => 'expense.transaction_reference',
'vendor' => 'vendor_id', 'vendor' => 'expense.vendor_id',
'invoice' => 'invoice_id', 'invoice' => 'expense.invoice_id',
'user' => 'expense.user',
'assigned_user' => 'expense.assigned_user',
]; ];
private array $decorate_keys = [ private array $decorate_keys = [
@ -120,13 +122,17 @@ class ExpenseExport extends BaseExport
$entity = []; $entity = [];
foreach (array_values($this->input['report_keys']) as $key) { foreach (array_values($this->input['report_keys']) as $key) {
$parts = explode('.', $key);
$keyval = array_search($key, $this->entity_keys); $keyval = array_search($key, $this->entity_keys);
if (array_key_exists($key, $transformed_expense)) { if (is_array($parts) && $parts[0] == 'expense' && array_key_exists($parts[1], $transformed_expense)) {
$entity[$keyval] = $transformed_expense[$key]; $entity[$key] = $transformed_expense[$parts[1]];
} elseif (array_key_exists($key, $transformed_expense)) {
$entity[$key] = $transformed_expense[$key];
} else { } else {
$entity[$keyval] = ''; $entity[$key] = $this->resolveKey($key, $expense, $this->expense_transformer);
} }
} }
return $this->decorateAdvancedFields($expense, $entity); return $this->decorateAdvancedFields($expense, $entity);
@ -134,32 +140,40 @@ class ExpenseExport extends BaseExport
private function decorateAdvancedFields(Expense $expense, array $entity) :array private function decorateAdvancedFields(Expense $expense, array $entity) :array
{ {
if (in_array('currency_id', $this->input['report_keys'])) { if (in_array('expense.currency_id', $this->input['report_keys'])) {
$entity['currency'] = $expense->currency ? $expense->currency->code : ''; $entity['expense.currency_id'] = $expense->currency ? $expense->currency->code : '';
} }
if (in_array('client_id', $this->input['report_keys'])) { if (in_array('expense.client_id', $this->input['report_keys'])) {
$entity['client'] = $expense->client ? $expense->client->present()->name() : ''; $entity['expense.client'] = $expense->client ? $expense->client->present()->name() : '';
} }
if (in_array('invoice_id', $this->input['report_keys'])) { if (in_array('expense.invoice_id', $this->input['report_keys'])) {
$entity['invoice'] = $expense->invoice ? $expense->invoice->number : ''; $entity['expense.invoice_id'] = $expense->invoice ? $expense->invoice->number : '';
} }
if (in_array('category_id', $this->input['report_keys'])) { if (in_array('expense.category', $this->input['report_keys'])) {
$entity['category'] = $expense->category ? $expense->category->name : ''; $entity['expense.category'] = $expense->category ? $expense->category->name : '';
} }
if (in_array('vendor_id', $this->input['report_keys'])) { if (in_array('expense.vendor_id', $this->input['report_keys'])) {
$entity['vendor'] = $expense->vendor ? $expense->vendor->name : ''; $entity['expense.vendor'] = $expense->vendor ? $expense->vendor->name : '';
} }
if (in_array('payment_type_id', $this->input['report_keys'])) { if (in_array('expense.payment_type_id', $this->input['report_keys'])) {
$entity['payment_type'] = $expense->payment_type ? $expense->payment_type->name : ''; $entity['expense.payment_type_id'] = $expense->payment_type ? $expense->payment_type->name : '';
} }
if (in_array('project_id', $this->input['report_keys'])) { if (in_array('expense.project_id', $this->input['report_keys'])) {
$entity['project'] = $expense->project ? $expense->project->name : ''; $entity['expense.project_id'] = $expense->project ? $expense->project->name : '';
}
if (in_array('expense.user', $this->input['report_keys'])) {
$entity['expense.user'] = $expense->user ? $expense->user->present()->name() : '';
}
if (in_array('expense.assigned_user', $this->input['report_keys'])) {
$entity['expense.assigned_user'] = $expense->assigned_user ? $expense->assigned_user->present()->name() : '';
} }
return $entity; return $entity;

View File

@ -0,0 +1,172 @@
<?php
/**
* PurchaseOrder Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. PurchaseOrder Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
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 App\Utils\Number;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
class PurchaseOrderExport extends BaseExport
{
private Company $company;
private $purchase_order_transformer;
public string $date_key = 'date';
public Writer $csv;
public array $entity_keys = [
'amount' => 'amount',
'balance' => 'balance',
'vendor' => 'vendor_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',
'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_id' => 'currency_id',
];
private array $decorate_keys = [
'country',
'currency_id',
'status',
'vendor',
'project',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
$this->input = $input;
$this->purchase_order_transformer = new PurchaseOrderTransformer();
}
public function run()
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$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);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = PurchaseOrder::query()
->withTrashed()
->with('vendor')
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);
// if(isset($this->input['status'])) {
// $query = $this->addPurchaseOrderStatusFilter($query, $this->input['status']);
// }
$query->cursor()
->each(function ($purchase_order) {
$this->csv->insertOne($this->buildRow($purchase_order));
});
return $this->csv->toString();
}
private function buildRow(PurchaseOrder $purchase_order) :array
{
$transformed_purchase_order = $this->purchase_order_transformer->transform($purchase_order);
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
if(!$keyval) {
$keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $key;
}
if(!$keyval) {
$keyval = $key;
}
if (array_key_exists($key, $transformed_purchase_order)) {
$entity[$keyval] = $transformed_purchase_order[$key];
} elseif (array_key_exists($keyval, $transformed_purchase_order)) {
$entity[$keyval] = $transformed_purchase_order[$keyval];
} else {
$entity[$keyval] = $this->resolveKey($keyval, $purchase_order, $this->purchase_order_transformer);
}
}
return $this->decorateAdvancedFields($purchase_order, $entity);
}
private function decorateAdvancedFields(PurchaseOrder $purchase_order, array $entity) :array
{
if (in_array('country_id', $this->input['report_keys'])) {
$entity['country'] = $purchase_order->vendor->country ? ctrans("texts.country_{$purchase_order->vendor->country->name}") : '';
}
if (in_array('currency_id', $this->input['report_keys'])) {
$entity['currency_id'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code;
}
if (in_array('vendor_id', $this->input['report_keys'])) {
$entity['vendor'] = $purchase_order->vendor->present()->name();
}
if (in_array('status_id', $this->input['report_keys'])) {
$entity['status'] = $purchase_order->stringStatus($purchase_order->status_id);
}
return $entity;
}
}

View File

@ -0,0 +1,246 @@
<?php
/**
* PurchaseOrder Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. PurchaseOrder Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
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\Support\Facades\App;
use League\Csv\Writer;
class PurchaseOrderItemExport extends BaseExport
{
private Company $company;
private $purchase_order_transformer;
public string $date_key = 'date';
public Writer $csv;
private bool $force_keys = false;
public array $entity_keys = [
'amount' => 'amount',
'balance' => 'balance',
'vendor' => 'vendor_id',
'vendor_number' => 'vendor.number',
'vendor_id_number' => 'vendor.id_number',
'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',
'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',
'quantity' => 'item.quantity',
'cost' => 'item.cost',
'product_key' => 'item.product_key',
'buy_price' => 'item.product_cost',
'notes' => 'item.notes',
'discount' => 'item.discount',
'is_amount_discount' => 'item.is_amount_discount',
'tax_rate1' => 'item.tax_rate1',
'tax_rate2' => 'item.tax_rate2',
'tax_rate3' => 'item.tax_rate3',
'tax_name1' => 'item.tax_name1',
'tax_name2' => 'item.tax_name2',
'tax_name3' => 'item.tax_name3',
'line_total' => 'item.line_total',
'gross_line_total' => 'item.gross_line_total',
// 'invoice1' => 'item.custom_value1',
// 'invoice2' => 'item.custom_value2',
// 'invoice3' => 'item.custom_value3',
// 'invoice4' => 'item.custom_value4',
'tax_category' => 'item.tax_id',
'type' => 'item.type_id',
];
private array $decorate_keys = [
'client',
'currency_id',
'status'
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
$this->input = $input;
$this->purchase_order_transformer = new PurchaseOrderTransformer();
}
public function run()
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$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->force_keys = true;
$this->input['report_keys'] = array_values($this->entity_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = PurchaseOrder::query()
->withTrashed()
->with('vendor')->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);
$query->cursor()
->each(function ($purchase_order) {
$this->iterateItems($purchase_order);
});
return $this->csv->toString();
}
private function iterateItems(PurchaseOrder $purchase_order)
{
$transformed_invoice = $this->buildRow($purchase_order);
$transformed_items = [];
foreach ($purchase_order->line_items as $item) {
$item_array = [];
foreach (array_values($this->input['report_keys']) as $key) { //items iterator produces item array
if (str_contains($key, "item.")) {
$key = str_replace("item.", "", $key);
$keyval = $key;
$keyval = str_replace("custom_value", "invoice", $key);
if($key == 'type_id') {
$keyval = 'type';
}
if($key == 'tax_id') {
$keyval = 'tax_category';
}
if (property_exists($item, $key)) {
$item_array[$keyval] = $item->{$key};
} else {
$item_array[$keyval] = '';
}
}
}
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) { //create an array of report keys only
$keyval = array_search($key, $this->entity_keys);
if (array_key_exists($key, $transformed_items)) {
$entity[$keyval] = $transformed_items[$key];
} else {
$entity[$keyval] = "";
}
}
$transformed_items = array_merge($transformed_invoice, $item_array);
$entity = $this->decorateAdvancedFields($purchase_order, $transformed_items);
$this->csv->insertOne($entity);
}
}
private function buildRow(PurchaseOrder $purchase_order) :array
{
$transformed_invoice = $this->purchase_order_transformer->transform($purchase_order);
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
if(!$keyval) {
$keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $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];
} else {
$entity[$keyval] = $this->resolveKey($keyval, $purchase_order, $this->purchase_order_transformer);
}
}
return $this->decorateAdvancedFields($purchase_order, $entity);
}
private function decorateAdvancedFields(PurchaseOrder $purchase_order, array $entity) :array
{
if (in_array('currency_id', $this->input['report_keys'])) {
$entity['currency'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code;
}
if(array_key_exists('type', $entity)) {
$entity['type'] = $purchase_order->typeIdString($entity['type']);
}
if(array_key_exists('tax_category', $entity)) {
$entity['tax_category'] = $purchase_order->taxTypeString($entity['tax_category']);
}
if($this->force_keys) {
$entity['vendor'] = $purchase_order->vendor->present()->name();
$entity['vendor_id_number'] = $purchase_order->vendor->id_number;
$entity['vendor_number'] = $purchase_order->vendor->number;
$entity['status'] = $purchase_order->stringStatus($purchase_order->status_id);
}
return $entity;
}
}

View File

@ -0,0 +1,172 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Export\CSV;
use App\Libraries\MultiDB;
use App\Models\Vendor;
use App\Models\Company;
use App\Transformers\VendorContactTransformer;
use App\Transformers\VendorTransformer;
use App\Utils\Ninja;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
class VendorExport extends BaseExport
{
private $company;
private $vendor_transformer;
private $contact_transformer;
public Writer $csv;
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 array $forced_keys = [
// 'vendor.status'
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
$this->input = $input;
$this->vendor_transformer = new VendorTransformer();
$this->contact_transformer = new VendorContactTransformer();
}
public function run()
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$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);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Vendor::query()->with('contacts')
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);
$query->cursor()
->each(function ($vendor) {
$this->csv->insertOne($this->buildRow($vendor));
});
return $this->csv->toString();
}
private function buildRow(Vendor $vendor) :array
{
$transformed_contact = false;
$transformed_vendor = $this->vendor_transformer->transform($vendor);
if ($contact = $vendor->contacts()->first()) {
$transformed_contact = $this->contact_transformer->transform($contact);
}
$entity = [];
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]];
} else {
$entity[$keyval] = '';
}
}
return $this->decorateAdvancedFields($vendor, $entity);
}
private function decorateAdvancedFields(Vendor $vendor, array $entity) :array
{
if (in_array('vendor.country_id', $this->input['report_keys'])) {
$entity['country'] = $vendor->country ? ctrans("texts.country_{$vendor->country->name}") : '';
}
if (in_array('vendor.currency', $this->input['report_keys'])) {
$entity['currency'] = $vendor->currency() ? $vendor->currency()->code : $vendor->company->currency()->code;
}
$entity['status'] = $this->calculateStatus($vendor);
return $entity;
}
private function calculateStatus($vendor)
{
if ($vendor->is_deleted) {
return ctrans('texts.deleted');
}
if ($vendor->deleted_at) {
return ctrans('texts.archived');
}
return ctrans('texts.active');
}
}

View File

@ -55,6 +55,10 @@ class InvoiceFilters extends QueryFilters
$this->builder->where(function ($query) use ($status_parameters) { $this->builder->where(function ($query) use ($status_parameters) {
$invoice_filters = []; $invoice_filters = [];
if (in_array('draft', $status_parameters)) {
$invoice_filters[] = Invoice::STATUS_DRAFT;
}
if (in_array('paid', $status_parameters)) { if (in_array('paid', $status_parameters)) {
$invoice_filters[] = Invoice::STATUS_PAID; $invoice_filters[] = Invoice::STATUS_PAID;
} }
@ -131,19 +135,6 @@ class InvoiceFilters extends QueryFilters
} }
/**
* @return Builder
* @throws RuntimeException
*/
public function without_deleted_clients(): Builder
{
return $this->builder->whereHas('client', function ($query) {
$query->where('is_deleted', 0);
});
}
/** /**
* @return Builder * @return Builder
* @return Builder * @return Builder

View File

@ -288,6 +288,33 @@ abstract class QueryFilters
return $this->builder; return $this->builder;
} }
/**
* @return Builder
* @throws RuntimeException
*/
public function without_deleted_clients(): Builder
{
return $this->builder->where(function ($query) {
$query->whereHas('client', function ($sub_query) {
$sub_query->where('is_deleted', 0);
})->orWhere('client_id', null);
});
}
/**
* @return Builder
* @throws RuntimeException
*/
public function without_deleted_vendors(): Builder
{
return $this->builder->where(function ($query) {
$query->whereHas('vendor', function ($sub_query) {
$sub_query->where('is_deleted', 0);
})->orWhere('vendor_id', null);
});
}
public function with(string $value = ''): Builder public function with(string $value = ''): Builder
{ {
if (strlen($value) == 0) { if (strlen($value) == 0) {

View File

@ -92,46 +92,6 @@ class ActivityController extends BaseController
->company() ->company()
->take($default_activities); ->take($default_activities);
// if ($request->has('react')) {
// /** @var \App\Models\User auth()->user() */
// $user = auth()->user();
// if (!$user->isAdmin()) {
// $activities->where('user_id', auth()->user()->id);
// }
// $system = ctrans('texts.system');
// $data = $activities->cursor()->map(function ($activity) {
// $arr =
// [
// 'client' => $activity->client ? $activity->client : '',
// 'contact' => $activity->client ? $activity->contact : '',
// 'quote' => $activity->quote ? $activity->quote : '',
// 'user' => $activity->user ? $activity->user : '',
// 'expense' => $activity->expense ? $activity->expense : '',
// 'invoice' => $activity->invoice ? $activity->invoice : '',
// 'recurring_invoice' => $activity->recurring_invoice ? $activity->recurring_invoice : '',
// 'payment' => $activity->payment ? $activity->payment : '',
// 'credit' => $activity->credit ? $activity->credit : '',
// 'task' => $activity->task ? $activity->task : '',
// 'vendor' => $activity->vendor ? $activity->vendor : '',
// 'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '',
// 'subscription' => $activity->subscription ? $activity->subscription : '',
// 'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
// 'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense : '',
// ];
// $activity_array = $activity->toArray();
// return array_merge($arr, $activity_array);
// });
// return response()->json(['data' => $data->toArray()], 200);
// }
// else
if($request->has('reactv2')) { if($request->has('reactv2')) {
/** @var \App\Models\User auth()->user() */ /** @var \App\Models\User auth()->user() */

View File

@ -11,22 +11,29 @@
namespace App\Http\Controllers\ClientPortal; namespace App\Http\Controllers\ClientPortal;
use App\Events\Invoice\InvoiceWasViewed;
use App\Events\Misc\InvitationWasViewed;
use App\Http\Controllers\Controller;
use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest;
use App\Http\Requests\ClientPortal\Invoices\ShowInvoiceRequest;
use App\Http\Requests\ClientPortal\Invoices\ShowInvoicesRequest;
use App\Models\Invoice;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Number; use App\Utils\Number;
use App\Utils\Traits\MakesDates; use App\Models\Invoice;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View; use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Models\QuoteInvitation;
use App\Utils\Traits\MakesHash;
use App\Models\CreditInvitation;
use App\Utils\Traits\MakesDates;
use App\Models\InvoiceInvitation;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Contracts\View\Factory;
use App\Models\PurchaseOrderInvitation;
use Illuminate\Support\Facades\Storage;
use App\Events\Invoice\InvoiceWasViewed;
use App\Events\Misc\InvitationWasViewed;
use App\Models\RecurringInvoiceInvitation;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Http\Requests\ClientPortal\Invoices\ShowInvoiceRequest;
use App\Http\Requests\ClientPortal\Invoices\ShowInvoicesRequest;
use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest;
class InvoiceController extends Controller class InvoiceController extends Controller
{ {
@ -77,6 +84,31 @@ class InvoiceController extends Controller
return $this->render('invoices.show', $data); return $this->render('invoices.show', $data);
} }
public function showBlob($hash)
{
$data = Cache::pull($hash);
match($data['entity_type']){
'invoice' => $invitation = InvoiceInvitation::withTrashed()->find($data['invitation_id']),
'quote' => $invitation = QuoteInvitation::withTrashed()->find($data['invitation_id']),
'credit' => $invitation = CreditInvitation::withTrashed()->find($data['invitation_id']),
'recurring_invoice' => $invitation = RecurringInvoiceInvitation::withTrashed()->find($data['invitation_id']),
};
$file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle();
// $headers = ['Content-Type' => 'application/pdf'];
// $entity_string = $data['entity_type'];
// $file_name = $invitation->{$entity_string}->numberFormatter().'.pdf';
// return response()->streamDownload(function () use ($file) {
// echo $file;
// }, $file_name, $headers);
$headers = ['Content-Type' => 'application/pdf'];
return response()->make($file, 200, $headers);
}
/** /**
* Pay one or more invoices. * Pay one or more invoices.
* *

View File

@ -0,0 +1,53 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers\Reports;
use App\Export\CSV\PurchaseOrderItemExport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
class PurchaseOrderItemReportController extends BaseController
{
use MakesHash;
private string $filename = 'purchase_order_items.csv';
public function __construct()
{
parent::__construct();
}
public function __invoke(GenericReportRequest $request)
{
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), PurchaseOrderItemExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new PurchaseOrderItemExport(auth()->user()->company(), $request->all());
$csv = $export->run();
$headers = [
'Content-Disposition' => 'attachment',
'Content-Type' => 'text/csv',
];
return response()->streamDownload(function () use ($csv) {
echo $csv;
}, $this->filename, $headers);
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* PurchaseOrder Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. PurchaseOrder Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers\Reports;
use App\Export\CSV\PurchaseOrderExport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
use App\Jobs\Report\SendToAdmin;
use App\Utils\Traits\MakesHash;
class PurchaseOrderReportController extends BaseController
{
use MakesHash;
private string $filename = 'purchase_orders.csv';
public function __construct()
{
parent::__construct();
}
public function __invoke(GenericReportRequest $request)
{
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), PurchaseOrderExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new PurchaseOrderExport(auth()->user()->company(), $request->all());
$csv = $export->run();
$headers = [
'Content-Disposition' => 'attachment',
'Content-Type' => 'text/csv',
];
return response()->streamDownload(function () use ($csv) {
echo $csv;
}, $this->filename, $headers);
}
}

View File

@ -29,37 +29,6 @@ class RecurringInvoiceReportController extends BaseController
parent::__construct(); parent::__construct();
} }
/**
* @OA\Post(
* path="/api/v1/reports/recurring_invoices",
* operationId="getRecurringInvoiceReport",
* tags={"reports"},
* summary="Recurring Invoice reports",
* description="Export recurring invoice reports",
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(ref="#/components/schemas/GenericReportSchema")
* ),
* @OA\Response(
* response=200,
* description="success",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function __invoke(GenericReportRequest $request) public function __invoke(GenericReportRequest $request)
{ {
if ($request->has('send_email') && $request->get('send_email')) { if ($request->has('send_email') && $request->get('send_email')) {

View File

@ -0,0 +1,53 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers\Reports;
use App\Export\CSV\VendorExport;
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
{
use MakesHash;
private string $filename = 'vendors.csv';
public function __construct()
{
parent::__construct();
}
public function __invoke(GenericReportRequest $request)
{
if ($request->has('send_email') && $request->get('send_email')) {
SendToAdmin::dispatch(auth()->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());
$csv = $export->run();
$headers = [
'Content-Disposition' => 'attachment',
'Content-Type' => 'text/csv',
];
return response()->streamDownload(function () use ($csv) {
echo $csv;
}, $this->filename, $headers);
}
}

View File

@ -11,21 +11,24 @@
namespace App\Http\Controllers\VendorPortal; namespace App\Http\Controllers\VendorPortal;
use App\Events\Misc\InvitationWasViewed; use App\Utils\Ninja;
use App\Events\PurchaseOrder\PurchaseOrderWasAccepted; use Illuminate\View\View;
use App\Events\PurchaseOrder\PurchaseOrderWasViewed; use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesDates;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\VendorPortal\PurchaseOrders\ProcessPurchaseOrdersInBulkRequest; use App\Jobs\Invoice\InjectSignature;
use Illuminate\Support\Facades\Cache;
use Illuminate\Contracts\View\Factory;
use App\Models\PurchaseOrderInvitation;
use Illuminate\Support\Facades\Storage;
use App\Events\Misc\InvitationWasViewed;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
use App\Events\PurchaseOrder\PurchaseOrderWasAccepted;
use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrderRequest; use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrderRequest;
use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrdersRequest; use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrdersRequest;
use App\Jobs\Invoice\InjectSignature; use App\Http\Requests\VendorPortal\PurchaseOrders\ProcessPurchaseOrdersInBulkRequest;
use App\Models\PurchaseOrder;
use App\Utils\Ninja;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class PurchaseOrderController extends Controller class PurchaseOrderController extends Controller
{ {
@ -108,6 +111,28 @@ class PurchaseOrderController extends Controller
return $this->render('purchase_orders.show', $data); return $this->render('purchase_orders.show', $data);
} }
public function showBlob($hash)
{
$data = Cache::pull($hash);
$invitation = PurchaseOrderInvitation::withTrashed()->find($data['invitation_id']);
$file = (new CreatePurchaseOrderPdf($invitation, $invitation->company->db))->rawPdf();
// $headers = ['Content-Type' => 'application/pdf'];
// $entity_string = $data['entity_type'];
// $file_name = $invitation->{$entity_string}->numberFormatter().'.pdf';
// return response()->streamDownload(function () use ($file) {
// echo $file;
// }, $file_name, $headers);
$headers = ['Content-Type' => 'application/pdf'];
return response()->make($file, 200, $headers);
}
private function sidebarMenu() :array private function sidebarMenu() :array
{ {
$enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules; $enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;

View File

@ -16,6 +16,7 @@ use App\Utils\Number;
use Livewire\Component; use Livewire\Component;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use Illuminate\Support\Str;
use App\Models\QuoteInvitation; use App\Models\QuoteInvitation;
use App\Utils\VendorHtmlEngine; use App\Utils\VendorHtmlEngine;
use App\Models\CreditInvitation; use App\Models\CreditInvitation;
@ -23,6 +24,7 @@ use App\Services\Pdf\PdfBuilder;
use App\Services\Pdf\PdfService; use App\Services\Pdf\PdfService;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Services\Pdf\PdfDesigner; use App\Services\Pdf\PdfDesigner;
use Illuminate\Support\Facades\Cache;
use App\Services\Pdf\PdfConfiguration; use App\Services\Pdf\PdfConfiguration;
use App\Models\PurchaseOrderInvitation; use App\Models\PurchaseOrderInvitation;
use App\Models\RecurringInvoiceInvitation; use App\Models\RecurringInvoiceInvitation;
@ -48,6 +50,12 @@ class PdfSlot extends Component
protected $listeners = ['viewportChanged' => 'getPdf']; protected $listeners = ['viewportChanged' => 'getPdf'];
public $show_cost = true;
public $show_quantity = true;
public $route_entity = 'client';
public function mount() public function mount()
{ {
MultiDB::setDb($this->db); MultiDB::setDb($this->db);
@ -55,7 +63,21 @@ class PdfSlot extends Component
public function getPdf() public function getPdf()
{ {
$this->pdf = $this->entity->fullscreenPdfViewer($this->invitation); // $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation);
$blob = [
'entity_type' => $this->resolveEntityType(),
'entity_id' => $this->entity->id,
'invitation_id' => $this->invitation->id,
'download' => false,
];
$hash = Str::random(64);
Cache::put($hash, $blob, now()->addMinutes(2));
$this->pdf = $hash;
} }
public function downloadPdf() public function downloadPdf()
@ -78,10 +100,19 @@ class PdfSlot extends Component
public function render() public function render()
{ {
$this->entity_type = $this->resolveEntityType(); $this->entity_type = $this->resolveEntityType();
$this->settings = $this->entity->client ? $this->entity->client->getMergedSettings() : $this->entity->company->settings; $this->settings = $this->entity->client ? $this->entity->client->getMergedSettings() : $this->entity->company->settings;
$this->show_cost = in_array('$product.unit_cost', $this->settings->pdf_variables->product_columns);
$this->show_quantity = in_array('$product.quantity', $this->settings->pdf_variables->product_columns);
if($this->entity_type == 'quote' && !$this->settings->sync_invoice_quote_columns ){
$this->show_cost = in_array('$product.unit_cost', $this->settings->pdf_variables->product_quote_columns);
$this->show_quantity = in_array('$product.quantity', $this->settings->pdf_variables->product_quote_columns);
}
$this->html_variables = $this->entity->client ? $this->html_variables = $this->entity->client ?
(new HtmlEngine($this->invitation))->generateLabelsAndValues() : (new HtmlEngine($this->invitation))->generateLabelsAndValues() :
(new VendorHtmlEngine($this->invitation))->generateLabelsAndValues(); (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
@ -100,7 +131,6 @@ class PdfSlot extends Component
'entity_details' => $this->getEntityDetails(), 'entity_details' => $this->getEntityDetails(),
'user_details' => $this->getUserDetails(), 'user_details' => $this->getUserDetails(),
'user_name' => $this->getUserName(), 'user_name' => $this->getUserName(),
]); ]);
} }
@ -243,6 +273,7 @@ class PdfSlot extends Component
} elseif ($this->invitation instanceof RecurringInvoiceInvitation) { } elseif ($this->invitation instanceof RecurringInvoiceInvitation) {
return 'recurring_invoice'; return 'recurring_invoice';
} elseif ($this->invitation instanceof PurchaseOrderInvitation) { } elseif ($this->invitation instanceof PurchaseOrderInvitation) {
$this->route_entity = 'vendor';
return 'purchase_order'; return 'purchase_order';
} }

View File

@ -257,7 +257,8 @@ class RequiredClientInfo extends Component
} }
if (Str::startsWith($field['name'], 'contact_')) { if (Str::startsWith($field['name'], 'contact_')) {
if (empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) {
if (empty($this->contact->{$_field}) || is_null($this->contact->{$_field}) || str_contains($this->contact->{$_field}, '@example.com')) {
$this->show_form = true; $this->show_form = true;
} else { } else {
$this->fields[$index]['filled'] = true; $this->fields[$index]['filled'] = true;

View File

@ -71,6 +71,7 @@ class UpdateInvoiceRequest extends Request
$rules['tax_name1'] = 'bail|sometimes|string|nullable'; $rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['status_id'] = 'bail|sometimes|not_in:5'; //do not all cancelled invoices to be modfified.
// not needed. // not needed.
// $rules['partial_due_date'] = 'bail|sometimes|required_unless:partial,0,null'; // $rules['partial_due_date'] = 'bail|sometimes|required_unless:partial,0,null';
@ -100,7 +101,8 @@ class UpdateInvoiceRequest extends Request
public function messages() public function messages()
{ {
return [ return [
'id' => ctrans('text.locked_invoice'), 'id' => ctrans('texts.locked_invoice'),
'status_id' => ctrans('texts.locked_invoice'),
]; ];
} }
} }

View File

@ -69,13 +69,16 @@ class ValidCreditsRules implements Rule
if (! $cred) { if (! $cred) {
$this->error_msg = ctrans('texts.credit_not_found'); $this->error_msg = ctrans('texts.credit_not_found');
return false; return false;
} }
if ($cred->client_id != $this->input['client_id']) { if ($cred->client_id != $this->input['client_id']) {
$this->error_msg = ctrans('texts.invoices_dont_match_client'); $this->error_msg = ctrans('texts.invoices_dont_match_client');
return false;
}
if($cred->balance < $credit['amount']) {
$this->error_msg = ctrans('texts.insufficient_credit_balance');
return false; return false;
} }
} }

View File

@ -1173,11 +1173,26 @@ class Import implements ShouldQueue
unset($modified['id']); unset($modified['id']);
$credit = $credit_repository->save( $credit = $credit_repository->save(
$modified, $modified,
CreditFactory::create($this->company->id, $modified['user_id']) CreditFactory::create($this->company->id, $modified['user_id'])
); );
if($credit->status_id == 4)
{
$client = $credit->client;
$client->balance -= $credit->balance;
$client->credit_balance -= $credit->amount;
$client->saveQuietly();
$credit->paid_to_date = $credit->amount;
$credit->balance = 0;
$credit->saveQuietly();
}
//remove credit balance from ledger //remove credit balance from ledger
if ($credit->balance > 0 && $credit->client->balance > 0) { if ($credit->balance > 0 && $credit->client->balance > 0) {
$client = $credit->client; $client = $credit->client;

View File

@ -188,7 +188,7 @@ class CreatePurchaseOrderPdf implements ShouldQueue
} }
if (config('ninja.log_pdf_html')) { if (config('ninja.log_pdf_html')) {
info($maker->getCompiledHTML()); nlog($maker->getCompiledHTML());
} }
$maker = null; $maker = null;

View File

@ -275,4 +275,19 @@ class Expense extends BaseModel
return $this->belongsTo(BankTransaction::class); return $this->belongsTo(BankTransaction::class);
} }
public function stringStatus()
{
if($this->is_deleted)
return ctrans('texts.deleted');
elseif($this->payment_date)
return ctrans('texts.paid');
elseif($this->invoice_id)
return ctrans('texts.invoiced');
elseif($this->should_be_invoiced)
return ctrans('texts.pending');
elseif($this->trashed())
return ctrans('texts.archived');
return ctrans('texts.logged');
}
} }

View File

@ -108,50 +108,6 @@ use Laracasts\Presenter\PresentableTrait;
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @mixin \Eloquent * @mixin \Eloquent
*/ */
class Vendor extends BaseModel class Vendor extends BaseModel

View File

@ -57,7 +57,7 @@ class RouteServiceProvider extends ServiceProvider
if (Ninja::isSelfHost()) { if (Ninja::isSelfHost()) {
return Limit::none(); return Limit::none();
} else { } else {
return Limit::perMinute(50)->by($request->ip()); return Limit::perMinute(30)->by($request->ip());
} }
}); });
@ -89,6 +89,11 @@ class RouteServiceProvider extends ServiceProvider
return Limit::perMinute(2)->by($request->ip()); return Limit::perMinute(2)->by($request->ip());
}); });
RateLimiter::for('portal', function (Request $request) {
return Limit::perMinute(15)->by($request->ip());
});
} }
/** /**

View File

@ -50,6 +50,9 @@ class ActivityRepository extends BaseRepository
$activity->{$key} = $value; $activity->{$key} = $value;
} }
if($entity->company)
$activity->account_id = $entity->company->account_id;
if ($token_id = $this->getTokenId($event_vars)) { if ($token_id = $this->getTokenId($event_vars)) {
$activity->token_id = $token_id; $activity->token_id = $token_id;
} }

View File

@ -44,7 +44,6 @@ class HandleRestore extends AbstractService
//cannot restore an invoice with a deleted payment //cannot restore an invoice with a deleted payment
foreach ($this->invoice->payments as $payment) { foreach ($this->invoice->payments as $payment) {
if (($this->invoice->paid_to_date == 0) && $payment->is_deleted) { if (($this->invoice->paid_to_date == 0) && $payment->is_deleted) {
$this->invoice->delete(); //set it back to deleted so that it can be restored from repository
return $this->invoice; return $this->invoice;
} }
} }
@ -81,8 +80,8 @@ class HandleRestore extends AbstractService
Paymentable::query() Paymentable::query()
->withTrashed() ->withTrashed()
->where('payment_id', $payment->id) ->where('payment_id', $payment->id)
->where('paymentable_type', '=', 'invoices') // ->where('paymentable_type', '=', 'invoices')
->where('paymentable_id', $this->invoice->id) // ->where('paymentable_id', $this->invoice->id)
->update(['deleted_at' => null]); ->update(['deleted_at' => null]);
}); });
@ -102,6 +101,12 @@ class HandleRestore extends AbstractService
->where('paymentable_type', '=', 'invoices') ->where('paymentable_type', '=', 'invoices')
->where('paymentable_id', $this->invoice->id) ->where('paymentable_id', $this->invoice->id)
->sum(DB::raw('refunded')); ->sum(DB::raw('refunded'));
//14/07/2023 - do not include credits in the payment amount
$this->adjustment_amount -= $payment->paymentables
->where('paymentable_type', '=', 'App\Models\Credit')
->sum(DB::raw('amount'));
} }
$this->total_payments = $this->invoice->payments->sum('amount') - $this->invoice->payments->sum('refunded'); $this->total_payments = $this->invoice->payments->sum('amount') - $this->invoice->payments->sum('refunded');
@ -130,11 +135,16 @@ class HandleRestore extends AbstractService
->where('paymentable_id', $this->invoice->id) ->where('paymentable_id', $this->invoice->id)
->sum(DB::raw('refunded')); ->sum(DB::raw('refunded'));
$payment_adjustment -= $payment->paymentables
->where('paymentable_type', '=', 'App\Models\Credit')
->sum(DB::raw('amount'));
$payment->amount += $payment_adjustment; $payment->amount += $payment_adjustment;
$payment->applied += $payment_adjustment; $payment->applied += $payment_adjustment;
$payment->is_deleted = false; $payment->is_deleted = false;
$payment->restore(); $payment->restore();
$payment->saveQuietly(); $payment->saveQuietly();
}); });
return $this; return $this;

View File

@ -11,11 +11,12 @@
namespace App\Services\Invoice; namespace App\Services\Invoice;
use App\Jobs\Inventory\AdjustProductInventory; use App\Models\Credit;
use App\Models\Invoice; use App\Models\Invoice;
use App\Services\AbstractService; use App\Services\AbstractService;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use App\Utils\Traits\GeneratesCounter;
use App\Jobs\Inventory\AdjustProductInventory;
class MarkInvoiceDeleted extends AbstractService class MarkInvoiceDeleted extends AbstractService
{ {
@ -94,6 +95,11 @@ class MarkInvoiceDeleted extends AbstractService
->where('paymentable_id', $this->invoice->id) ->where('paymentable_id', $this->invoice->id)
->sum(DB::raw('refunded')); ->sum(DB::raw('refunded'));
//14-07-2023 - Do not include credits in the payment adjustment.
$payment_adjustment -= $payment->paymentables
->where('paymentable_type', '=', 'App\Models\Credit')
->sum(DB::raw('amount'));
$payment->amount -= $payment_adjustment; $payment->amount -= $payment_adjustment;
$payment->applied -= $payment_adjustment; $payment->applied -= $payment_adjustment;
$payment->save(); $payment->save();

View File

@ -109,6 +109,8 @@ class DeletePayment
$paymentable_invoice->service() $paymentable_invoice->service()
->updatePaidToDate($net_deletable * -1) ->updatePaidToDate($net_deletable * -1)
->save(); ->save();
$paymentable_invoice->delete();
} }
}); });
} }

View File

@ -274,6 +274,10 @@ trait DesignHelpers
// Some variables don't map 1:1 to table columns. This gives us support for such cases. // Some variables don't map 1:1 to table columns. This gives us support for such cases.
$aliases = [ $aliases = [
'$quote.balance_due' => 'partial', '$quote.balance_due' => 'partial',
'$purchase_order.po_number' => 'number',
'$purchase_order.total' => 'amount',
'$purchase_order.due_date' => 'due_date',
'$purchase_order.balance_due' => 'balance_due',
]; ];
try { try {

View File

@ -132,7 +132,7 @@ class Ninja
'ip' => $ip, 'ip' => $ip,
'token' => request()->header('X-API-TOKEN'), 'token' => request()->header('X-API-TOKEN'),
'is_system' => app()->runningInConsole(), 'is_system' => app()->runningInConsole(),
'user_id' => $user_id, 'user_id' => ($ip == '127.0.0.1') ? null : $user_id,
]; ];
} }

View File

@ -133,16 +133,18 @@ class VendorHtmlEngine
$data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->company->date_format(), $this->company->locale()) ?: '&nbsp;', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')]; $data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->company->date_format(), $this->company->locale()) ?: '&nbsp;', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
$data['$dueDate'] = &$data['$due_date']; $data['$dueDate'] = &$data['$due_date'];
$data['$purchase_order.due_date'] = &$data['$due_date'];
$data['$payment_due'] = ['value' => $this->translateDate($this->entity->due_date, $this->company->date_format(), $this->company->locale()) ?: '&nbsp;', 'label' => ctrans('texts.payment_due')]; $data['$payment_due'] = ['value' => $this->translateDate($this->entity->due_date, $this->company->date_format(), $this->company->locale()) ?: '&nbsp;', 'label' => ctrans('texts.payment_due')];
$data['$poNumber'] = ['value' => $this->entity->po_number, 'label' => ctrans('texts.po_number')]; $data['$purchase_order.po_number'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.po_number')];
$data['$poNumber'] = &$data['$purchase_order.po_number'];
$data['$entity.datetime'] = ['value' => $this->formatDatetime($this->entity->created_at, $this->company->date_format()), 'label' => ctrans('texts.date')]; $data['$entity.datetime'] = ['value' => $this->formatDatetime($this->entity->created_at, $this->company->date_format()), 'label' => ctrans('texts.date')];
$data['$po_number'] = &$data['$poNumber'];
$data['$status_logo'] = ['value' => ' ', 'label' => ' ']; $data['$status_logo'] = ['value' => ' ', 'label' => ' '];
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.purchase_order')]; $data['$entity'] = ['value' => '', 'label' => ctrans('texts.purchase_order')];
$data['$number'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.purchase_order_number')]; $data['$number'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.purchase_order_number_short')]; $data['$number_short'] = ['value' => $this->entity->number ?: '&nbsp;', 'label' => ctrans('texts.purchase_order_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->company) ?: '', 'label' => ctrans('texts.invoice_terms')]; $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->company) ?: '', 'label' => ctrans('texts.invoice_terms')];
$data['$terms'] = &$data['$entity.terms']; $data['$terms'] = &$data['$entity.terms'];
@ -155,7 +157,6 @@ class VendorHtmlEngine
$data['$purchase_order.number'] = &$data['$number']; $data['$purchase_order.number'] = &$data['$number'];
$data['$purchase_order.date'] = &$data['$date']; $data['$purchase_order.date'] = &$data['$date'];
$data['$purchase_order.po_number'] = &$data['$poNumber'];
$data['$purchase_order.due_date'] = &$data['$due_date']; $data['$purchase_order.due_date'] = &$data['$due_date'];
$data['$entity_issued_to'] = ['value' => '', 'label' => ctrans("texts.purchase_order_issued_to")]; $data['$entity_issued_to'] = ['value' => '', 'label' => ctrans("texts.purchase_order_issued_to")];
@ -189,8 +190,9 @@ class VendorHtmlEngine
} }
} }
// $data['$balance_due'] = $data['$balance_due'];
$data['$outstanding'] = &$data['$balance_due']; $data['$outstanding'] = &$data['$balance_due'];
$data['$purchase_order.balance_due'] = &$data['$balance_due'];
$data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->vendor) ?: '&nbsp;', 'label' => ctrans('texts.partial_due')]; $data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->vendor) ?: '&nbsp;', 'label' => ctrans('texts.partial_due')];
$data['$partial'] = &$data['$partial_due']; $data['$partial'] = &$data['$partial_due'];

View File

@ -95,7 +95,8 @@
"turbo124/predis": "1.1.11", "turbo124/predis": "1.1.11",
"twilio/sdk": "^6.40", "twilio/sdk": "^6.40",
"webpatser/laravel-countries": "dev-master#75992ad", "webpatser/laravel-countries": "dev-master#75992ad",
"wepay/php-sdk": "^0.3" "wepay/php-sdk": "^0.3",
"psr/http-message": "^1.0"
}, },
"require-dev": { "require-dev": {
"php": "^8.1", "php": "^8.1",

255
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "135eec9ab7a1e8c0ab3820ff27cf1488", "content-hash": "be16996524279f340c44e2f6c9a0ba53",
"packages": [ "packages": [
{ {
"name": "adrienrn/php-mimetyper", "name": "adrienrn/php-mimetyper",
@ -424,16 +424,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.269.0", "version": "3.275.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78" "reference": "54dcef3349c81b46c0f5f6e54b5f9bfb5db19903"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/54dcef3349c81b46c0f5f6e54b5f9bfb5db19903",
"reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78", "reference": "54dcef3349c81b46c0f5f6e54b5f9bfb5db19903",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -445,7 +445,8 @@
"guzzlehttp/promises": "^1.4.0", "guzzlehttp/promises": "^1.4.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"mtdowling/jmespath.php": "^2.6", "mtdowling/jmespath.php": "^2.6",
"php": ">=5.5" "php": ">=5.5",
"psr/http-message": "^1.0"
}, },
"require-dev": { "require-dev": {
"andrewsville/php-token-reflection": "^1.4", "andrewsville/php-token-reflection": "^1.4",
@ -462,7 +463,6 @@
"paragonie/random_compat": ">= 2", "paragonie/random_compat": ">= 2",
"phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5", "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5",
"psr/cache": "^1.0", "psr/cache": "^1.0",
"psr/http-message": "^1.0",
"psr/simple-cache": "^1.0", "psr/simple-cache": "^1.0",
"sebastian/comparator": "^1.2.3 || ^4.0", "sebastian/comparator": "^1.2.3 || ^4.0",
"yoast/phpunit-polyfills": "^1.0" "yoast/phpunit-polyfills": "^1.0"
@ -513,9 +513,9 @@
"support": { "support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.269.0" "source": "https://github.com/aws/aws-sdk-php/tree/3.275.7"
}, },
"time": "2023-04-26T18:21:04+00:00" "time": "2023-07-13T18:21:04+00:00"
}, },
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@ -2485,16 +2485,16 @@
}, },
{ {
"name": "google/apiclient-services", "name": "google/apiclient-services",
"version": "v0.307.0", "version": "v0.308.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git", "url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "5f7d451aa912355dda61aa29ac3a4afa8b252467" "reference": "85cf00383e6bf6eca131bd3261b7859ea418a578"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/5f7d451aa912355dda61aa29ac3a4afa8b252467", "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/85cf00383e6bf6eca131bd3261b7859ea418a578",
"reference": "5f7d451aa912355dda61aa29ac3a4afa8b252467", "reference": "85cf00383e6bf6eca131bd3261b7859ea418a578",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2523,9 +2523,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues", "issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.307.0" "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.308.0"
}, },
"time": "2023-07-01T01:02:13+00:00" "time": "2023-07-09T01:06:13+00:00"
}, },
{ {
"name": "google/auth", "name": "google/auth",
@ -4554,16 +4554,16 @@
}, },
{ {
"name": "laravel/socialite", "name": "laravel/socialite",
"version": "v5.6.3", "version": "v5.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/socialite.git", "url": "https://github.com/laravel/socialite.git",
"reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e" "reference": "f5996f499e14db15407201a6bfbaba3ce6ce736c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/socialite/zipball/00ea7f8630673ea49304fc8a9fca5a64eb838c7e", "url": "https://api.github.com/repos/laravel/socialite/zipball/f5996f499e14db15407201a6bfbaba3ce6ce736c",
"reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e", "reference": "f5996f499e14db15407201a6bfbaba3ce6ce736c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4620,7 +4620,7 @@
"issues": "https://github.com/laravel/socialite/issues", "issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite" "source": "https://github.com/laravel/socialite"
}, },
"time": "2023-06-06T13:42:43+00:00" "time": "2023-07-08T20:51:43+00:00"
}, },
{ {
"name": "laravel/tinker", "name": "laravel/tinker",
@ -5768,16 +5768,16 @@
}, },
{ {
"name": "mollie/mollie-api-php", "name": "mollie/mollie-api-php",
"version": "v2.57.0", "version": "v2.58.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mollie/mollie-api-php.git", "url": "https://github.com/mollie/mollie-api-php.git",
"reference": "be201657b00a197e5238bbec81fad2a0fc0b83e4" "reference": "5120e5b3e4622a290b64acf87266ea47d10d7301"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/be201657b00a197e5238bbec81fad2a0fc0b83e4", "url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/5120e5b3e4622a290b64acf87266ea47d10d7301",
"reference": "be201657b00a197e5238bbec81fad2a0fc0b83e4", "reference": "5120e5b3e4622a290b64acf87266ea47d10d7301",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5854,9 +5854,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/mollie/mollie-api-php/issues", "issues": "https://github.com/mollie/mollie-api-php/issues",
"source": "https://github.com/mollie/mollie-api-php/tree/v2.57.0" "source": "https://github.com/mollie/mollie-api-php/tree/v2.58.0"
}, },
"time": "2023-06-23T09:09:15+00:00" "time": "2023-07-11T12:01:27+00:00"
}, },
{ {
"name": "moneyphp/money", "name": "moneyphp/money",
@ -7280,16 +7280,16 @@
}, },
{ {
"name": "php-http/discovery", "name": "php-http/discovery",
"version": "1.19.0", "version": "1.19.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-http/discovery.git", "url": "https://github.com/php-http/discovery.git",
"reference": "1856a119a0b0ba8da8b5c33c080aa7af8fac25b4" "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-http/discovery/zipball/1856a119a0b0ba8da8b5c33c080aa7af8fac25b4", "url": "https://api.github.com/repos/php-http/discovery/zipball/57f3de01d32085fea20865f9b16fb0e69347c39e",
"reference": "1856a119a0b0ba8da8b5c33c080aa7af8fac25b4", "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -7352,9 +7352,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/php-http/discovery/issues", "issues": "https://github.com/php-http/discovery/issues",
"source": "https://github.com/php-http/discovery/tree/1.19.0" "source": "https://github.com/php-http/discovery/tree/1.19.1"
}, },
"time": "2023-06-19T08:45:36+00:00" "time": "2023-07-11T07:02:26+00:00"
}, },
{ {
"name": "php-http/guzzle7-adapter", "name": "php-http/guzzle7-adapter",
@ -7844,16 +7844,16 @@
}, },
{ {
"name": "phpseclib/phpseclib", "name": "phpseclib/phpseclib",
"version": "3.0.20", "version": "3.0.21",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/phpseclib/phpseclib.git",
"reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67" "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67", "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1",
"reference": "543a1da81111a0bfd6ae7bbc2865c5e89ed3fc67", "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -7934,7 +7934,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpseclib/phpseclib/issues", "issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.20" "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21"
}, },
"funding": [ "funding": [
{ {
@ -7950,7 +7950,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-06-13T06:30:34+00:00" "time": "2023-07-09T15:24:48+00:00"
}, },
{ {
"name": "phpstan/phpdoc-parser", "name": "phpstan/phpdoc-parser",
@ -8360,16 +8360,16 @@
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
"version": "2.0", "version": "1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/http-message.git", "url": "https://github.com/php-fig/http-message.git",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -8378,7 +8378,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.0.x-dev" "dev-master": "1.1.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -8393,7 +8393,7 @@
"authors": [ "authors": [
{ {
"name": "PHP-FIG", "name": "PHP-FIG",
"homepage": "https://www.php-fig.org/" "homepage": "http://www.php-fig.org/"
} }
], ],
"description": "Common interface for HTTP messages", "description": "Common interface for HTTP messages",
@ -8407,9 +8407,9 @@
"response" "response"
], ],
"support": { "support": {
"source": "https://github.com/php-fig/http-message/tree/2.0" "source": "https://github.com/php-fig/http-message/tree/1.1"
}, },
"time": "2023-04-04T09:54:51+00:00" "time": "2023-04-04T09:50:52+00:00"
}, },
{ {
"name": "psr/log", "name": "psr/log",
@ -15117,16 +15117,16 @@
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
"version": "2.15.2", "version": "2.15.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/filp/whoops.git", "url": "https://github.com/filp/whoops.git",
"reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73" "reference": "c83e88a30524f9360b11f585f71e6b17313b7187"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187",
"reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", "reference": "c83e88a30524f9360b11f585f71e6b17313b7187",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -15176,7 +15176,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/filp/whoops/issues", "issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.15.2" "source": "https://github.com/filp/whoops/tree/2.15.3"
}, },
"funding": [ "funding": [
{ {
@ -15184,7 +15184,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-04-12T12:00:00+00:00" "time": "2023-07-13T12:00:00+00:00"
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
@ -15392,79 +15392,6 @@
}, },
"time": "2023-02-16T20:00:16+00:00" "time": "2023-02-16T20:00:16+00:00"
}, },
{
"name": "laravel/dusk",
"version": "v6.25.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/dusk.git",
"reference": "25a595ac3dc82089a91af10dd23b0d58fd3f6d0b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/dusk/zipball/25a595ac3dc82089a91af10dd23b0d58fd3f6d0b",
"reference": "25a595ac3dc82089a91af10dd23b0d58fd3f6d0b",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-zip": "*",
"illuminate/console": "^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
"nesbot/carbon": "^2.0",
"php": "^7.2|^8.0",
"php-webdriver/webdriver": "^1.9.0",
"symfony/console": "^4.3|^5.0|^6.0",
"symfony/finder": "^4.3|^5.0|^6.0",
"symfony/process": "^4.3|^5.0|^6.0",
"vlucas/phpdotenv": "^3.0|^4.0|^5.2"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.16|^5.17.1|^6.12.1|^7.0",
"phpunit/phpunit": "^7.5.15|^8.4|^9.0"
},
"suggest": {
"ext-pcntl": "Used to gracefully terminate Dusk when tests are running."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Dusk\\DuskServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Dusk\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel Dusk provides simple end-to-end testing and browser automation.",
"keywords": [
"laravel",
"testing",
"webdriver"
],
"support": {
"issues": "https://github.com/laravel/dusk/issues",
"source": "https://github.com/laravel/dusk/tree/v6.25.2"
},
"time": "2022-09-29T09:37:07+00:00"
},
{ {
"name": "maximebf/debugbar", "name": "maximebf/debugbar",
"version": "v1.18.2", "version": "v1.18.2",
@ -16067,72 +15994,6 @@
}, },
"time": "2022-02-21T01:04:05+00:00" "time": "2022-02-21T01:04:05+00:00"
}, },
{
"name": "php-webdriver/webdriver",
"version": "1.14.0",
"source": {
"type": "git",
"url": "https://github.com/php-webdriver/php-webdriver.git",
"reference": "3ea4f924afb43056bf9c630509e657d951608563"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3ea4f924afb43056bf9c630509e657d951608563",
"reference": "3ea4f924afb43056bf9c630509e657d951608563",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-zip": "*",
"php": "^7.3 || ^8.0",
"symfony/polyfill-mbstring": "^1.12",
"symfony/process": "^5.0 || ^6.0"
},
"replace": {
"facebook/webdriver": "*"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.20.0",
"ondram/ci-detector": "^4.0",
"php-coveralls/php-coveralls": "^2.4",
"php-mock/php-mock-phpunit": "^2.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpunit/phpunit": "^9.3",
"squizlabs/php_codesniffer": "^3.5",
"symfony/var-dumper": "^5.0 || ^6.0"
},
"suggest": {
"ext-SimpleXML": "For Firefox profile creation"
},
"type": "library",
"autoload": {
"files": [
"lib/Exception/TimeoutException.php"
],
"psr-4": {
"Facebook\\WebDriver\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
"homepage": "https://github.com/php-webdriver/php-webdriver",
"keywords": [
"Chromedriver",
"geckodriver",
"php",
"selenium",
"webdriver"
],
"support": {
"issues": "https://github.com/php-webdriver/php-webdriver/issues",
"source": "https://github.com/php-webdriver/php-webdriver/tree/1.14.0"
},
"time": "2023-02-09T12:12:19+00:00"
},
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",
"version": "5.3.0", "version": "5.3.0",
@ -16659,16 +16520,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "9.6.9", "version": "9.6.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "a9aceaf20a682aeacf28d582654a1670d8826778" "reference": "a6d351645c3fe5a30f5e86be6577d946af65a328"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a9aceaf20a682aeacf28d582654a1670d8826778", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a6d351645c3fe5a30f5e86be6577d946af65a328",
"reference": "a9aceaf20a682aeacf28d582654a1670d8826778", "reference": "a6d351645c3fe5a30f5e86be6577d946af65a328",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -16742,7 +16603,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.9" "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.10"
}, },
"funding": [ "funding": [
{ {
@ -16758,7 +16619,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-06-11T06:13:56+00:00" "time": "2023-07-10T04:04:23+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",

View File

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

View File

@ -0,0 +1,34 @@
<?php
use App\Models\Currency;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$tb = Currency::find(21);
if($tb) {
$tb->symbol = '฿';
$tb->save();
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
};

View File

@ -43,7 +43,7 @@ class CurrenciesSeeder extends Seeder
['id' => 18, 'name' => 'Guatemalan Quetzal', 'code' => 'GTQ', 'symbol' => 'Q', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 18, 'name' => 'Guatemalan Quetzal', 'code' => 'GTQ', 'symbol' => 'Q', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 19, 'name' => 'Malaysian Ringgit', 'code' => 'MYR', 'symbol' => 'RM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 19, 'name' => 'Malaysian Ringgit', 'code' => 'MYR', 'symbol' => 'RM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 20, 'name' => 'Brazilian Real', 'code' => 'BRL', 'symbol' => 'R$', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','], ['id' => 20, 'name' => 'Brazilian Real', 'code' => 'BRL', 'symbol' => 'R$', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','],
['id' => 21, 'name' => 'Thai Baht', 'code' => 'THB', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 21, 'name' => 'Thai Baht', 'code' => 'THB', 'symbol' => '฿', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 22, 'name' => 'Nigerian Naira', 'code' => 'NGN', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 22, 'name' => 'Nigerian Naira', 'code' => 'NGN', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 23, 'name' => 'Argentine Peso', 'code' => 'ARS', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','], ['id' => 23, 'name' => 'Argentine Peso', 'code' => 'ARS', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','],
['id' => 24, 'name' => 'Bangladeshi Taka', 'code' => 'BDT', 'symbol' => 'Tk', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 24, 'name' => 'Bangladeshi Taka', 'code' => 'BDT', 'symbol' => 'Tk', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],

View File

@ -753,7 +753,7 @@ $LANG = array(
'activity_7' => 'Klient :contact zobrazil fakturu :invoice pro :client', 'activity_7' => 'Klient :contact zobrazil fakturu :invoice pro :client',
'activity_8' => ':user archivoval fakturu :invoice', 'activity_8' => ':user archivoval fakturu :invoice',
'activity_9' => ':user smazal fakturu :invoice', 'activity_9' => ':user smazal fakturu :invoice',
'activity_10' => ':contact entered payment :payment for :payment_amount on invoice :invoice for :client', 'activity_10' => ':user entered payment :payment for :payment_amount on invoice :invoice for :client',
'activity_11' => ':user změnil platbu :payment', 'activity_11' => ':user změnil platbu :payment',
'activity_12' => ':user archivoval platbu :payment', 'activity_12' => ':user archivoval platbu :payment',
'activity_13' => ':user smazal platbu :payment', 'activity_13' => ':user smazal platbu :payment',

View File

@ -5122,7 +5122,8 @@ $LANG = array(
'lang_French - Swiss' => 'French - Swiss', 'lang_French - Swiss' => 'French - Swiss',
'currency_swazi_lilangeni' => 'Swazi Lilangeni', 'currency_swazi_lilangeni' => 'Swazi Lilangeni',
'income' => 'Income', 'income' => 'Income',
'amount_received_help' => 'Enter a value here if the total amount received was MORE than the invoice amount, or when recording a payment with no invoices. Otherwise this field should be left blank.',
'vendor_phone' => 'Vendor Phone',
); );

View File

@ -761,7 +761,7 @@ $LANG = array(
'activity_7' => ':contact viewed invoice :invoice for :client', 'activity_7' => ':contact viewed invoice :invoice for :client',
'activity_8' => ':user archived invoice :invoice', 'activity_8' => ':user archived invoice :invoice',
'activity_9' => ':user deleted invoice :invoice', 'activity_9' => ':user deleted invoice :invoice',
'activity_10' => ':contact entered payment :payment for :payment_amount on invoice :invoice for :client', 'activity_10' => ':user entered payment :payment for :payment_amount on invoice :invoice for :client',
'activity_11' => ':user updated payment :payment', 'activity_11' => ':user updated payment :payment',
'activity_12' => ':user archived payment :payment', 'activity_12' => ':user archived payment :payment',
'activity_13' => ':user deleted payment :payment', 'activity_13' => ':user deleted payment :payment',

View File

@ -748,7 +748,7 @@ $LANG = array(
'activity_7' => ':contact a visualisé la facture :invoice pour :client', 'activity_7' => ':contact a visualisé la facture :invoice pour :client',
'activity_8' => ':user a archivé la facture :invoice', 'activity_8' => ':user a archivé la facture :invoice',
'activity_9' => ':user a supprimé la facture :invoice', 'activity_9' => ':user a supprimé la facture :invoice',
'activity_10' => ':contact a saisi le paiement :payment de :payment_amount de la facture :invoice pour :client', 'activity_10' => ':user a saisi le paiement :payment de :payment_amount de la facture :invoice pour :client',
'activity_11' => ':user a mis à jour le paiement :payment', 'activity_11' => ':user a mis à jour le paiement :payment',
'activity_12' => ':user a archivé le paiement :payment', 'activity_12' => ':user a archivé le paiement :payment',
'activity_13' => ':user a supprimé le paiement :payment', 'activity_13' => ':user a supprimé le paiement :payment',
@ -1433,7 +1433,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'payment_type_Swish' => 'Swish', 'payment_type_Swish' => 'Swish',
'payment_type_Alipay' => 'Alipay', 'payment_type_Alipay' => 'Alipay',
'payment_type_Sofort' => 'Sofort', 'payment_type_Sofort' => 'Sofort',
'payment_type_SEPA' => 'SEPA Prélèvement automatique', 'payment_type_SEPA' => 'Prélèvement automatique SEPA',
'payment_type_Bitcoin' => 'Bitcoin', 'payment_type_Bitcoin' => 'Bitcoin',
'payment_type_GoCardless' => 'GoCardless', 'payment_type_GoCardless' => 'GoCardless',
'payment_type_Zelle' => 'Zelle', 'payment_type_Zelle' => 'Zelle',
@ -2448,7 +2448,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'alipay' => 'Alipay', 'alipay' => 'Alipay',
'sofort' => 'Sofort', 'sofort' => 'Sofort',
'sepa' => 'SEPA Prélèvement automatique', 'sepa' => 'Prélèvement automatique SEPA',
'name_without_special_characters' => 'Veuillez entrer un nom en utilisant seulement les lettres de a à z et des espaces.', 'name_without_special_characters' => 'Veuillez entrer un nom en utilisant seulement les lettres de a à z et des espaces.',
'enable_alipay' => 'Accepter Alipay', 'enable_alipay' => 'Accepter Alipay',
'enable_sofort' => 'Accepter les transferts d\'institutions bancaires de l\'Union européenne', 'enable_sofort' => 'Accepter les transferts d\'institutions bancaires de l\'Union européenne',
@ -3130,7 +3130,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'number_padding' => 'Disposition du numéro', 'number_padding' => 'Disposition du numéro',
'general' => 'Général', 'general' => 'Général',
'surcharge_field' => 'Champ Surcharge', 'surcharge_field' => 'Champ Surcharge',
'company_value' => 'Valeur de compagnie', 'company_value' => 'Valeur de l\'entreprise',
'credit_field' => 'Champ Crédit', 'credit_field' => 'Champ Crédit',
'payment_field' => 'Champ Paiement', 'payment_field' => 'Champ Paiement',
'group_field' => 'Champ Groupe', 'group_field' => 'Champ Groupe',
@ -3510,7 +3510,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'search_expenses' => 'Recherche de dépenses', 'search_expenses' => 'Recherche de dépenses',
'search_payments' => 'Recherche de paiements', 'search_payments' => 'Recherche de paiements',
'search_groups' => 'Recherche de groupes', 'search_groups' => 'Recherche de groupes',
'search_company' => 'Recherche d\'entreprises', 'search_company' => 'Recherche d\'une entreprise',
'cancelled_invoice' => 'La facture a été annulée', 'cancelled_invoice' => 'La facture a été annulée',
'cancelled_invoices' => 'Les factures ont été annulées', 'cancelled_invoices' => 'Les factures ont été annulées',
'reversed_invoice' => 'La facture a été inversée', 'reversed_invoice' => 'La facture a été inversée',
@ -3972,8 +3972,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'account_balance' => 'Solde de compte', 'account_balance' => 'Solde de compte',
'thanks' => 'Merci', 'thanks' => 'Merci',
'minimum_required_payment' => 'Le paiement minimum requis est :amount', 'minimum_required_payment' => 'Le paiement minimum requis est :amount',
'under_payments_disabled' => 'La société ne tolère pas le sous-paiement.', 'under_payments_disabled' => 'L\'entreprise ne tolère pas le sous-paiement.',
'over_payments_disabled' => 'La société ne tolère pas le sur-paiement.', 'over_payments_disabled' => 'L\'entreprise ne tolère pas le sur-paiement.',
'saved_at' => 'Enregistré à :time', 'saved_at' => 'Enregistré à :time',
'credit_payment' => 'Le crédit a été appliqué à la facture :invoice_number', 'credit_payment' => 'Le crédit a été appliqué à la facture :invoice_number',
'credit_subject' => 'Nouveau crédit :credit de :account', 'credit_subject' => 'Nouveau crédit :credit de :account',
@ -4109,7 +4109,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'migration_api_secret_notice' => 'Vous pouvez trouver API_SECRET dans le fichier .env ou Invoice Ninja v5. Si la propriété est manquante, laissez le champ vide.', 'migration_api_secret_notice' => 'Vous pouvez trouver API_SECRET dans le fichier .env ou Invoice Ninja v5. Si la propriété est manquante, laissez le champ vide.',
'billing_coupon_notice' => 'Votre rabais sera appliqué au moment de régler votre facture.', 'billing_coupon_notice' => 'Votre rabais sera appliqué au moment de régler votre facture.',
'use_last_email' => 'Utiliser le dernier e-mail', 'use_last_email' => 'Utiliser le dernier e-mail',
'activate_company' => 'Activer la société', 'activate_company' => 'Activer l\'entreprise',
'activate_company_help' => 'Activez les courriels, les factures récurrentes et les notifications', 'activate_company_help' => 'Activez les courriels, les factures récurrentes et les notifications',
'an_error_occurred_try_again' => 'Une erreur s\'est produite, veuillez réessayer', 'an_error_occurred_try_again' => 'Une erreur s\'est produite, veuillez réessayer',
'please_first_set_a_password' => 'Veuillez d\'abord définir un mot de passe', 'please_first_set_a_password' => 'Veuillez d\'abord définir un mot de passe',
@ -4242,9 +4242,9 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'giropay_law' => 'En saisissant vos information de client (tel que votre nom et numéro de compte), vous (le client) agréez à donner cette information volontairement.', 'giropay_law' => 'En saisissant vos information de client (tel que votre nom et numéro de compte), vous (le client) agréez à donner cette information volontairement.',
'klarna' => 'Klarna', 'klarna' => 'Klarna',
'eps' => 'EPS', 'eps' => 'EPS',
'becs' => 'BECS Prélèvement automatique', 'becs' => 'Prélèvement automatique BECS',
'bacs' => 'Débit direct BACS', 'bacs' => 'Prélèvement automatique BACS',
'payment_type_BACS' => 'Débit direct BACS', 'payment_type_BACS' => 'Prélèvement automatique BACS',
'missing_payment_method' => 'Veuillez ajouter une méthode de paiement avant de payer.', 'missing_payment_method' => 'Veuillez ajouter une méthode de paiement avant de payer.',
'becs_mandate' => 'En fournissant vos coordonnées bancaires, vous acceptez <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">l\'entente de service de requête de débit direct</a>, et autorisez Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 (“Stripe”) à prélever votre compte par le Bulk Electronic Clearing System (BECS) de la part du :company (“Merchant”) pour tout montant séparément communiqué à vous par le Marchand. Vous certifiez que vous êtes propriétaire du compte ou que vous êtes un signataire autorisé sur le compte indiqué plus haut.', 'becs_mandate' => 'En fournissant vos coordonnées bancaires, vous acceptez <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">l\'entente de service de requête de débit direct</a>, et autorisez Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 (“Stripe”) à prélever votre compte par le Bulk Electronic Clearing System (BECS) de la part du :company (“Merchant”) pour tout montant séparément communiqué à vous par le Marchand. Vous certifiez que vous êtes propriétaire du compte ou que vous êtes un signataire autorisé sur le compte indiqué plus haut.',
'you_need_to_accept_the_terms_before_proceeding' => 'Vous devez accepter les conditions pour continuer.', 'you_need_to_accept_the_terms_before_proceeding' => 'Vous devez accepter les conditions pour continuer.',
@ -4257,12 +4257,12 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'browser_pay' => 'Google Pay, Apple Pay et Microsoft Pay', 'browser_pay' => 'Google Pay, Apple Pay et Microsoft Pay',
'no_available_methods' => 'Cartes de crédit introuvable sur cet appareil. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Plus d\'information.</a>', 'no_available_methods' => 'Cartes de crédit introuvable sur cet appareil. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Plus d\'information.</a>',
'gocardless_mandate_not_ready' => 'La demande de paiement n\'est pas prête. Veuillez essayer de nouveau plus tard.', 'gocardless_mandate_not_ready' => 'La demande de paiement n\'est pas prête. Veuillez essayer de nouveau plus tard.',
'payment_type_instant_bank_pay' => 'Interac', 'payment_type_instant_bank_pay' => 'Instant Bank Pay',
'payment_type_iDEAL' => 'iDEAL', 'payment_type_iDEAL' => 'iDEAL',
'payment_type_Przelewy24' => 'Przelewy24', 'payment_type_Przelewy24' => 'Przelewy24',
'payment_type_Mollie Bank Transfer' => 'Virement bancaire Mollie', 'payment_type_Mollie Bank Transfer' => 'Virement bancaire Mollie',
'payment_type_KBC/CBC' => 'KBC/CBC', 'payment_type_KBC/CBC' => 'KBC/CBC',
'payment_type_Instant Bank Pay' => 'Interac', 'payment_type_Instant Bank Pay' => 'Instant Bank Pay',
'payment_type_Hosted Page' => 'Page hébergée', 'payment_type_Hosted Page' => 'Page hébergée',
'payment_type_GiroPay' => 'GiroPay', 'payment_type_GiroPay' => 'GiroPay',
'payment_type_EPS' => 'EPS', 'payment_type_EPS' => 'EPS',
@ -4279,7 +4279,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'show_pdf_preview_help' => 'Afficher l\'aperçu PDF lors de la rédaction des factures', 'show_pdf_preview_help' => 'Afficher l\'aperçu PDF lors de la rédaction des factures',
'print_pdf' => 'Imprimer le PDF', 'print_pdf' => 'Imprimer le PDF',
'remind_me' => 'Rappel', 'remind_me' => 'Rappel',
'instant_bank_pay' => 'Interac', 'instant_bank_pay' => 'Instant Bank Pay',
'click_selected' => 'Cliquer sur la sélection', 'click_selected' => 'Cliquer sur la sélection',
'hide_preview' => 'Cacher l\'aperçu', 'hide_preview' => 'Cacher l\'aperçu',
'edit_record' => 'Modifier l\'enregistrement', 'edit_record' => 'Modifier l\'enregistrement',
@ -5111,6 +5111,9 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'admin_initiated_payments_help' => 'Permet la saisie d\'un paiement dans le portal d\'administration sans facture', 'admin_initiated_payments_help' => 'Permet la saisie d\'un paiement dans le portal d\'administration sans facture',
'paid_date' => 'Date de paiement', 'paid_date' => 'Date de paiement',
'downloaded_entities' => 'Un courriel sera envoyé avec le ou les PDF', 'downloaded_entities' => 'Un courriel sera envoyé avec le ou les PDF',
'lang_French - Swiss' => 'Français - Suisse',
'currency_swazi_lilangeni' => 'Lilangeni Eswatinien',
'income' => 'Revenu',
); );

View File

@ -753,7 +753,7 @@ $LANG = array(
'activity_7' => ':contact が :client の請求書 :invoice を閲覧しました', 'activity_7' => ':contact が :client の請求書 :invoice を閲覧しました',
'activity_8' => ':user は 請求書 :invoice をアーカイブしました。', 'activity_8' => ':user は 請求書 :invoice をアーカイブしました。',
'activity_9' => ':user は 請求書 :invoice をアーカイブしました。', 'activity_9' => ':user は 請求書 :invoice をアーカイブしました。',
'activity_10' => ':contact は、:client の請求書 :invoice に :payment_amount の支払い :payment を入力しました', 'activity_10' => ':user は、:client の請求書 :invoice に :payment_amount の支払い :payment を入力しました',
'activity_11' => ':user updated payment :payment', 'activity_11' => ':user updated payment :payment',
'activity_12' => ':user archived payment :payment', 'activity_12' => ':user archived payment :payment',
'activity_13' => ':user deleted payment :payment', 'activity_13' => ':user deleted payment :payment',

View File

@ -754,7 +754,7 @@ $LANG = array(
'activity_7' => ':contact viewed invoice :invoice for :client', 'activity_7' => ':contact viewed invoice :invoice for :client',
'activity_8' => ':user archived invoice :invoice', 'activity_8' => ':user archived invoice :invoice',
'activity_9' => ':user deleted invoice :invoice', 'activity_9' => ':user deleted invoice :invoice',
'activity_10' => ':contact entered payment :payment for :payment_amount on invoice :invoice for :client', 'activity_10' => ':user entered payment :payment for :payment_amount on invoice :invoice for :client',
'activity_11' => ':user atnaujino mokėjimą :payment', 'activity_11' => ':user atnaujino mokėjimą :payment',
'activity_12' => ':user archived payment :payment', 'activity_12' => ':user archived payment :payment',
'activity_13' => ':user deleted payment :payment', 'activity_13' => ':user deleted payment :payment',

View File

@ -754,7 +754,7 @@ $LANG = array(
'activity_7' => ':contact har sett fakturaen :invoice for :client', 'activity_7' => ':contact har sett fakturaen :invoice for :client',
'activity_8' => ':user arkiverte faktura :invoice', 'activity_8' => ':user arkiverte faktura :invoice',
'activity_9' => ':user slettet faktura :invoice', 'activity_9' => ':user slettet faktura :invoice',
'activity_10' => ':contact la inn betaling :payment på :payment_amount', 'activity_10' => ':user la inn betaling :payment på :payment_amount',
'activity_11' => ':user oppdaterte betaling :payment', 'activity_11' => ':user oppdaterte betaling :payment',
'activity_12' => ':user arkiverte betaling :payment', 'activity_12' => ':user arkiverte betaling :payment',
'activity_13' => ':user slettet betaling :payment', 'activity_13' => ':user slettet betaling :payment',

View File

@ -751,7 +751,7 @@ $LANG = array(
'activity_7' => ':contact viewed invoice :invoice for :client', 'activity_7' => ':contact viewed invoice :invoice for :client',
'activity_8' => ':user ka arkivuar faturën :invoice', 'activity_8' => ':user ka arkivuar faturën :invoice',
'activity_9' => ':user ka fshirë faturën :invoice', 'activity_9' => ':user ka fshirë faturën :invoice',
'activity_10' => ':contact entered payment :payment for :payment_amount on invoice :invoice for :client', 'activity_10' => ':user entered payment :payment for :payment_amount on invoice :invoice for :client',
'activity_11' => ':user ka perditesuar pagesën :payment', 'activity_11' => ':user ka perditesuar pagesën :payment',
'activity_12' => ':user ka arkivuar pagesën :payment', 'activity_12' => ':user ka arkivuar pagesën :payment',
'activity_13' => ':user ka fshirë pagesën :payment', 'activity_13' => ':user ka fshirë pagesën :payment',

View File

@ -753,7 +753,7 @@ adresine gönderildi. Müthiş tüm özelliklerin kilidini açmak için lütfen
'activity_7' => ':contact viewed invoice :invoice for :client', 'activity_7' => ':contact viewed invoice :invoice for :client',
'activity_8' => ':user :invoice nolu faturayı arşivledi', 'activity_8' => ':user :invoice nolu faturayı arşivledi',
'activity_9' => ':user :invoice nolu faturayı sildi', 'activity_9' => ':user :invoice nolu faturayı sildi',
'activity_10' => ':contact entered payment :payment for :payment_amount on invoice :invoice for :client', 'activity_10' => ':user entered payment :payment for :payment_amount on invoice :invoice for :client',
'activity_11' => ':user :payment tutarlı ödemeyi güncelledi', 'activity_11' => ':user :payment tutarlı ödemeyi güncelledi',
'activity_12' => ':user :payment tutarlı ödemeyi arşivledi', 'activity_12' => ':user :payment tutarlı ödemeyi arşivledi',
'activity_13' => ':user :payment tutarlı ödemeyi sildi', 'activity_13' => ':user :payment tutarlı ödemeyi sildi',

View File

@ -3,10 +3,10 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache'; const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache'; const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = { const RESOURCES = {
"/": "faecf4eb3989415f8ac174b178967581", "/": "0aa10ee6dcb142946702cadae9812704",
"flutter.js": "a85fcf6324d3c4d3ae3be1ae4931e9c5", "flutter.js": "a85fcf6324d3c4d3ae3be1ae4931e9c5",
"favicon.png": "dca91c54388f52eded692718d5a98b8b", "favicon.png": "dca91c54388f52eded692718d5a98b8b",
"main.dart.js": "b0022e7084e6d7ec1e1a04e5d9f20501", "main.dart.js": "23a2e1cbb7cb043fd5ef1e51de367bb6",
"version.json": "bf49df736fed3f74ade0dbaebf08de11", "version.json": "bf49df736fed3f74ade0dbaebf08de11",
"canvaskit/profiling/canvaskit.js": "c21852696bc1cc82e8894d851c01921a", "canvaskit/profiling/canvaskit.js": "c21852696bc1cc82e8894d851c01921a",
"canvaskit/profiling/canvaskit.wasm": "371bc4e204443b0d5e774d64a046eb99", "canvaskit/profiling/canvaskit.wasm": "371bc4e204443b0d5e774d64a046eb99",

View File

@ -77,6 +77,7 @@ var Payment = /*#__PURE__*/function () {
}, { }, {
key: "displaySignature", key: "displaySignature",
value: function displaySignature() { value: function displaySignature() {
document.getElementById("signature-next-step").disabled = true;
var displaySignatureModal = document.getElementById("displaySignatureModal"); var displaySignatureModal = document.getElementById("displaySignatureModal");
displaySignatureModal.removeAttribute("style"); displaySignatureModal.removeAttribute("style");
var signaturePad = new SignaturePad(document.getElementById("signature-pad"), { var signaturePad = new SignaturePad(document.getElementById("signature-pad"), {
@ -91,7 +92,6 @@ var Payment = /*#__PURE__*/function () {
key: "handle", key: "handle",
value: function handle() { value: function handle() {
var _this2 = this; var _this2 = this;
document.getElementById("signature-next-step").disabled = true;
document.querySelectorAll(".dropdown-gateway-button").forEach(function (element) { document.querySelectorAll(".dropdown-gateway-button").forEach(function (element) {
element.addEventListener("click", function () { element.addEventListener("click", function () {
if (!_this2.submitting) { if (!_this2.submitting) {

View File

@ -54,7 +54,12 @@ var Accept = /*#__PURE__*/function () {
value: function handle() { value: function handle() {
var _this = this; var _this = this;
document.getElementById("signature-next-step").disabled = true; document.getElementById("signature-next-step").disabled = true;
document.getElementById("close-button").addEventListener('click', function () {
var approveButton = document.getElementById("approve-button");
if (approveButton) approveButton.disabled = false;
});
document.getElementById('approve-button').addEventListener('click', function () { document.getElementById('approve-button').addEventListener('click', function () {
console.log("accepted ");
if (_this.shouldDisplaySignature && _this.shouldDisplayTerms) { if (_this.shouldDisplaySignature && _this.shouldDisplayTerms) {
_this.displaySignature(); _this.displaySignature();
document.getElementById('signature-next-step').addEventListener('click', function () { document.getElementById('signature-next-step').addEventListener('click', function () {

View File

@ -61,12 +61,14 @@ var Approve = /*#__PURE__*/function () {
value: function handle() { value: function handle() {
var _this = this; var _this = this;
document.getElementById("signature-next-step").disabled = true; document.getElementById("signature-next-step").disabled = true;
document.getElementById("close_button").addEventListener('click', function () { document.getElementById("close-button").addEventListener('click', function () {
var approveButton = document.getElementById("approve-button"); var approveButton = document.getElementById("approve-button");
console.log('close button');
if (approveButton) approveButton.disabled = false; if (approveButton) approveButton.disabled = false;
}); });
document.getElementById("hide_close").addEventListener('click', function () { document.getElementById("close-terms-button").addEventListener('click', function () {
var approveButton = document.getElementById("approve-button"); var approveButton = document.getElementById("approve-button");
console.log('close terms-button');
if (approveButton) approveButton.disabled = false; if (approveButton) approveButton.disabled = false;
}); });
document.getElementById('approve-button').addEventListener('click', function () { document.getElementById('approve-button').addEventListener('click', function () {

146211
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

137261
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,13 +9,13 @@
"/js/clients/payments/stripe-bacs.js": "/js/clients/payments/stripe-bacs.js?id=a82927936510bbecc60de3e9742f9739", "/js/clients/payments/stripe-bacs.js": "/js/clients/payments/stripe-bacs.js?id=a82927936510bbecc60de3e9742f9739",
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=b7601721afa9599518ba894a0f88f947", "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=b7601721afa9599518ba894a0f88f947",
"/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js?id=b9c510dffa8420879983f04cd521564c", "/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js?id=b9c510dffa8420879983f04cd521564c",
"/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=d13b4a7934c9f7ddf2154f8f233843f7", "/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=e9040b9f5e9bbf5d04b8a02a284d0481",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=1bd5f983e7d9394fe9ac603dea94e901", "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=edeab1566d21cb93901d571bdcbb7c30",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=f0252b9b140b667794c252ae200cd844", "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=f0252b9b140b667794c252ae200cd844",
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=360b39d82a1a7dd90a8351776fbd9edb", "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=360b39d82a1a7dd90a8351776fbd9edb",
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=a2168c43060a7de40da20b5fc599bcab", "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=a2168c43060a7de40da20b5fc599bcab",
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=4158693089b29ee8e13cb7d9ce4480a9", "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=4158693089b29ee8e13cb7d9ce4480a9",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=4e596cec23cdd6487534e6ed5499d791", "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=4091846cf28ded010380cc2678fbe831",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=bfa116c1df42c641bc7a3ff4fa8d50dd", "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=bfa116c1df42c641bc7a3ff4fa8d50dd",
"/js/setup/setup.js": "/js/setup/setup.js?id=086b9e114b0b9ee01f909d686f489162", "/js/setup/setup.js": "/js/setup/setup.js?id=086b9e114b0b9ee01f909d686f489162",
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=cf50b5ba1fcd1d184bf0c10d710672c8", "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=cf50b5ba1fcd1d184bf0c10d710672c8",

View File

@ -84,6 +84,8 @@ class Payment {
} }
displaySignature() { displaySignature() {
document.getElementById("signature-next-step").disabled = true;
let displaySignatureModal = document.getElementById( let displaySignatureModal = document.getElementById(
"displaySignatureModal" "displaySignatureModal"
); );
@ -104,7 +106,6 @@ class Payment {
} }
handle() { handle() {
document.getElementById("signature-next-step").disabled = true;
document document
.querySelectorAll(".dropdown-gateway-button") .querySelectorAll(".dropdown-gateway-button")

View File

@ -48,9 +48,20 @@ class Accept {
document.getElementById("signature-next-step").disabled = true; document.getElementById("signature-next-step").disabled = true;
document.getElementById("close-button").addEventListener('click', () => {
const approveButton = document.getElementById("approve-button");
if (approveButton)
approveButton.disabled = false;
});
document document
.getElementById('approve-button') .getElementById('approve-button')
.addEventListener('click', () => { .addEventListener('click', () => {
console.log("accepted ");
if (this.shouldDisplaySignature && this.shouldDisplayTerms) { if (this.shouldDisplaySignature && this.shouldDisplayTerms) {
this.displaySignature(); this.displaySignature();

View File

@ -55,22 +55,27 @@ class Approve {
document.getElementById("signature-next-step").disabled = true; document.getElementById("signature-next-step").disabled = true;
document.getElementById("close_button").addEventListener('click', () => { document.getElementById("close-button").addEventListener('click', () => {
const approveButton = document.getElementById("approve-button"); const approveButton = document.getElementById("approve-button");
console.log('close button');
if(approveButton) if(approveButton)
approveButton.disabled = false; approveButton.disabled = false;
}); });
document.getElementById("hide_close").addEventListener('click', () => { document.getElementById("close-terms-button").addEventListener('click', () => {
const approveButton = document.getElementById("approve-button"); const approveButton = document.getElementById("approve-button");
if(approveButton) console.log('close terms-button');
if (approveButton)
approveButton.disabled = false; approveButton.disabled = false;
}); });
document document
.getElementById('approve-button') .getElementById('approve-button')
.addEventListener('click', () => { .addEventListener('click', () => {

View File

@ -1,4 +1,4 @@
@push('head')
<style> <style>
table, th, td { table, th, td {
@ -26,6 +26,7 @@ span {
} }
</style> </style>
@endpush
<div class="w-full bg-white py-3 border-2 shadow sm:rounded-lg"> <div class="w-full bg-white py-3 border-2 shadow sm:rounded-lg">
@ -43,7 +44,6 @@ span {
</div> </div>
<div id="user-details" class="mt-3 px-3 border-b-2 border-fuschia-600 flex flex-col items-end"> <div id="user-details" class="mt-3 px-3 border-b-2 border-fuschia-600 flex flex-col items-end">
<div x-data="{ show_user: false }" class="mb-3"> <div x-data="{ show_user: false }" class="mb-3">
@ -77,7 +77,15 @@ span {
<td> <td>
<div class="product-information"> <div class="product-information">
<div class="item-details"> <div class="item-details">
<p class="mt-2">{{ $product['quantity'] }} × {{ $product['cost'] }}</p> <p class="mt-2">
@if($show_quantity)
{{ $product['quantity'] }} x
@endif
@if($show_cost)
{{ $product['cost'] }}
@endif
</p>
<p class="overflow-ellipsis overflow-hidden px-1 mb-2">{{ $product['notes'] }}</p> <p class="overflow-ellipsis overflow-hidden px-1 mb-2">{{ $product['notes'] }}</p>
</div> </div>
</div> </div>
@ -174,10 +182,10 @@ span {
{{ strip_tags($entity->footer) }} {{ strip_tags($entity->footer) }}
</div> </div>
</div> </div>
@endif @endif
@push('head')
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
@ -224,3 +232,4 @@ span {
}); });
</script> </script>
@endpush

View File

@ -1,5 +1,5 @@
<div> <div>
<div class="flex flex-col items-end mb-2"> <div class="flex flex-col items-end mb-2">
<button wire:loading.attr="disabled" wire:click="downloadPdf" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-2 rounded inline-flex"> <button wire:loading.attr="disabled" wire:click="downloadPdf" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-2 rounded inline-flex">
<span>{{ ctrans('texts.download_pdf') }}</span> <span>{{ ctrans('texts.download_pdf') }}</span>
<div wire:loading wire:target="downloadPdf"> <div wire:loading wire:target="downloadPdf">
@ -10,11 +10,12 @@
</div> </div>
</button> </button>
</div> </div>
<div class="hidden lg:block"> <div class="hidden lg:block">
<div wire:init="getPdf()"> <div wire:init="getPdf()">
@if($pdf) @if($pdf)
<iframe id="pdf-iframe" src="{!! $pdf !!}" class="h-screen w-full border-0 mt-4"></iframe> <!-- <iframe id="pdf-iframe" src="{!! $pdf !!}" class="h-screen w-full border-0 mt-4"></iframe> -->
<iframe id="pdf-iframe" src="/{{ $route_entity }}/showBlob/{{ $pdf }}" class="h-screen w-full border-0 mt-4"></iframe>
@else @else
<div class="flex mt-4 place-items-center"> <div class="flex mt-4 place-items-center">
<span class="loader m-auto"></span> <span class="loader m-auto"></span>
@ -55,9 +56,9 @@
</div> </div>
@endif @endif
</div> </div>
</div> </div>
<div class="block lg:hidden"> <div class="block lg:hidden">
@include('portal.ninja2020.components.html-viewer') @include('portal.ninja2020.components.html-viewer')
</div> </div>
</div> </div>

View File

@ -4,8 +4,8 @@
@endphp @endphp
@push('head') @push('head')
<meta name="pdf-url" content="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}"> <!-- <meta name="pdf-url" content="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}">
<script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script> <script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script> -->
@endpush @endpush
<div class="flex items-center justify-between mt-4"> <div class="flex items-center justify-between mt-4">
@ -56,18 +56,18 @@
</section> </section>
</div> </div>
@if($mobile) <!-- @if($mobile)
<div class="w-full h-full overflow-auto mt-4"> <div class="w-full h-full overflow-auto mt-4">
<canvas id="pdf-placeholder" class="shadow rounded-lg bg-white"></canvas> <canvas id="pdf-placeholder" class="shadow rounded-lg bg-white"></canvas>
</div> </div>
@else @else -->
@livewire('pdf-slot', ['entity' => $entity, 'invitation' => $invitation, 'db' => $invitation->company->db]) @livewire('pdf-slot', ['entity' => $entity, 'invitation' => $invitation, 'db' => $invitation->company->db])
<!-- <iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}" class="h-screen w-full border-0 mt-4"></iframe> --> <!-- <iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}" class="h-screen w-full border-0 mt-4"></iframe> -->
@endif <!-- @endif -->
@if($mobile) @if($mobile)
@push('footer') @push('footer')
<script src="{{ asset('js/clients/shared/pdf.js') }}" defer></script> <!-- <script src="{{ asset('js/clients/shared/pdf.js') }}" defer></script> -->
@endpush @endpush
@endif @endif

View File

@ -32,8 +32,7 @@
</div> </div>
@include('portal.ninja2020.components.entity-documents', ['entity' => $credit]) @include('portal.ninja2020.components.entity-documents', ['entity' => $credit])
@livewire('pdf-slot', ['entity' => $credit, 'invitation' => $invitation, 'db' => $invitation->company->db])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $credit, 'invitation' => $invitation])
@endsection @endsection

View File

@ -1,4 +1,4 @@
<div style="display: none;" id="displaySignatureModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center"> <div style="display: none;" id="displaySignatureModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-data>
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 transition-opacity"> <div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div> </div>
@ -21,14 +21,14 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse" >
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto"> <div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto" x-data>
<button type="button" id="signature-next-step" class="button button-primary bg-primary" @click="document.getElementById('displaySignatureModal').style.display = 'none';"> <button type="button" id="signature-next-step" class="button button-primary bg-primary" @click="document.getElementById('displaySignatureModal').style.display = 'none';">
{{ ctrans('texts.next_step') }} {{ ctrans('texts.next_step') }}
</button> </button>
</div> </div>
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" id="close_button"> <div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" x-data>
<button @click="document.getElementById('displaySignatureModal').style.display = 'none';" type="button" class="button button-secondary"> <button @click="document.getElementById('displaySignatureModal').style.display = 'none';" type="button" class="button button-secondary" id="close-button">
{{ ctrans('texts.close') }} {{ ctrans('texts.close') }}
</button> </button>
</div> </div>

View File

@ -1,4 +1,4 @@
<div style="display: none;" id="displayTermsModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center"> <div style="display: none;" id="displayTermsModal" class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-data>
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 transition-opacity"> <div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div> </div>
@ -24,7 +24,7 @@
</div> </div>
</div> </div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto"> <div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto" x-data>
<button <button
type="button" type="button"
id="accept-terms-button" id="accept-terms-button"
@ -33,8 +33,8 @@
{{ ctrans('texts.i_agree') }} {{ ctrans('texts.i_agree') }}
</button> </button>
</div> </div>
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" id="hide_close"> <div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" x-data>
<button @click="document.getElementById('displayTermsModal').style.display = 'none';" type="button" class="button button-secondary"> <button @click="document.getElementById('displayTermsModal').style.display = 'none';" type="button" class="button button-secondary" id="close-terms-button">
{{ ctrans('texts.close') }} {{ ctrans('texts.close') }}
</button> </button>
</div> </div>

View File

@ -5,7 +5,6 @@
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}"> <meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}"> <meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
@include('portal.ninja2020.components.no-cache') @include('portal.ninja2020.components.no-cache')
<script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script> <script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script>
@endpush @endpush
@ -96,18 +95,26 @@
@endif @endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $invoice]) @include('portal.ninja2020.components.entity-documents', ['entity' => $invoice])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $invoice, 'invitation' => $invitation]) @livewire('pdf-slot', ['entity' => $invoice, 'invitation' => $invitation, 'db' => $invitation->company->db])
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$invoice], 'entity_type' => ctrans('texts.invoice')])
@include('portal.ninja2020.invoices.includes.signature')
@endsection @endsection
@section('footer') @section('footer')
<script src="{{ asset('js/clients/invoices/payment.js') }}"></script> @include('portal.ninja2020.invoices.includes.signature')
<script src="{{ asset('vendor/clipboard.min.js') }}"></script> @include('portal.ninja2020.invoices.includes.terms', ['entities' => [$invoice], 'entity_type' => ctrans('texts.invoice')])
@endsection
@push('head')
<script src="{{ asset('js/clients/invoices/payment.js') }}" defer></script>
<script src="{{ asset('vendor/clipboard.min.js') }}" defer></script>
<script type="text/javascript"> <script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
var clipboard = new ClipboardJS('.btn'); var clipboard = new ClipboardJS('.btn');
});
</script> </script>
@endsection @endpush

View File

@ -46,19 +46,27 @@
@endif @endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $purchase_order]) @include('portal.ninja2020.components.entity-documents', ['entity' => $purchase_order])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $purchase_order, 'invitation' => $invitation]) @livewire('pdf-slot', ['entity' => $purchase_order, 'invitation' => $invitation, 'db' => $invitation->company->db])
@endsection
@section('footer')
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$purchase_order], 'entity_type' => ctrans('texts.purchase_order')]) @include('portal.ninja2020.invoices.includes.terms', ['entities' => [$purchase_order], 'entity_type' => ctrans('texts.purchase_order')])
@include('portal.ninja2020.invoices.includes.signature') @include('portal.ninja2020.invoices.includes.signature')
@endsection @endsection
@section('footer') @push('head')
<script src="{{ asset('js/clients/purchase_orders/accept.js') }}"></script> <script src="{{ asset('js/clients/purchase_orders/accept.js') }}" defer></script>
<script src="{{ asset('vendor/clipboard.min.js') }}"></script> <script src="{{ asset('vendor/clipboard.min.js') }}" defer></script>
<script type="text/javascript"> <script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
var clipboard = new ClipboardJS('.btn'); var clipboard = new ClipboardJS('.btn');
</script> });
@endsection
</script>
@endpush

View File

@ -1,5 +1,5 @@
<div style="display: none;" id="displaySignatureModal" <div style="display: none;" id="displaySignatureModal"
class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center"> class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" x-data>
<div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" <div x-show="open" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"

View File

@ -106,19 +106,27 @@
@endif @endif
@include('portal.ninja2020.components.entity-documents', ['entity' => $quote]) @include('portal.ninja2020.components.entity-documents', ['entity' => $quote])
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $quote, 'invitation' => $invitation]) @livewire('pdf-slot', ['entity' => $quote, 'invitation' => $invitation, 'db' => $invitation->company->db])
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$quote], 'entity_type' => ctrans('texts.quote')])
@include('portal.ninja2020.invoices.includes.signature')
@include('portal.ninja2020.quotes.includes.user-input')
@endsection @endsection
@section('footer') @section('footer')
<script src="{{ asset('js/clients/quotes/approve.js') }}"></script> @include('portal.ninja2020.quotes.includes.user-input')
<script src="{{ asset('vendor/clipboard.min.js') }}"></script> @include('portal.ninja2020.invoices.includes.terms', ['entities' => [$quote], 'entity_type' => ctrans('texts.quote')])
@include('portal.ninja2020.invoices.includes.signature')
@endsection
<script type="text/javascript"> @push('head')
<script src="{{ asset('js/clients/quotes/approve.js') }}" defer></script>
<script src="{{ asset('vendor/clipboard.min.js') }}" defer></script>
<script type="text/javascript" defer>
document.addEventListener('DOMContentLoaded', () => {
var clipboard = new ClipboardJS('.btn'); var clipboard = new ClipboardJS('.btn');
});
</script> </script>
@endsection @endpush

View File

@ -75,6 +75,7 @@ use App\Http\Controllers\HostedMigrationController;
use App\Http\Controllers\ConnectedAccountController; use App\Http\Controllers\ConnectedAccountController;
use App\Http\Controllers\RecurringExpenseController; use App\Http\Controllers\RecurringExpenseController;
use App\Http\Controllers\RecurringInvoiceController; use App\Http\Controllers\RecurringInvoiceController;
use App\Http\Controllers\ProtectedDownloadController;
use App\Http\Controllers\ClientGatewayTokenController; use App\Http\Controllers\ClientGatewayTokenController;
use App\Http\Controllers\Reports\TaskReportController; use App\Http\Controllers\Reports\TaskReportController;
use App\Http\Controllers\Auth\ForgotPasswordController; use App\Http\Controllers\Auth\ForgotPasswordController;
@ -85,6 +86,7 @@ use App\Http\Controllers\Auth\PasswordTimeoutController;
use App\Http\Controllers\PreviewPurchaseOrderController; use App\Http\Controllers\PreviewPurchaseOrderController;
use App\Http\Controllers\Reports\ClientReportController; use App\Http\Controllers\Reports\ClientReportController;
use App\Http\Controllers\Reports\CreditReportController; use App\Http\Controllers\Reports\CreditReportController;
use App\Http\Controllers\Reports\VendorReportController;
use App\Http\Controllers\Reports\ExpenseReportController; use App\Http\Controllers\Reports\ExpenseReportController;
use App\Http\Controllers\Reports\InvoiceReportController; use App\Http\Controllers\Reports\InvoiceReportController;
use App\Http\Controllers\Reports\PaymentReportController; use App\Http\Controllers\Reports\PaymentReportController;
@ -101,11 +103,12 @@ use App\Http\Controllers\Support\Messages\SendingController;
use App\Http\Controllers\Reports\ClientSalesReportController; use App\Http\Controllers\Reports\ClientSalesReportController;
use App\Http\Controllers\Reports\InvoiceItemReportController; use App\Http\Controllers\Reports\InvoiceItemReportController;
use App\Http\Controllers\PaymentNotificationWebhookController; use App\Http\Controllers\PaymentNotificationWebhookController;
use App\Http\Controllers\ProtectedDownloadController;
use App\Http\Controllers\Reports\ProductSalesReportController; use App\Http\Controllers\Reports\ProductSalesReportController;
use App\Http\Controllers\Reports\ClientBalanceReportController; use App\Http\Controllers\Reports\ClientBalanceReportController;
use App\Http\Controllers\Reports\ClientContactReportController; use App\Http\Controllers\Reports\ClientContactReportController;
use App\Http\Controllers\Reports\PurchaseOrderReportController;
use App\Http\Controllers\Reports\RecurringInvoiceReportController; use App\Http\Controllers\Reports\RecurringInvoiceReportController;
use App\Http\Controllers\Reports\PurchaseOrderItemReportController;
Route::group(['middleware' => ['throttle:api', 'api_secret_check']], function () { Route::group(['middleware' => ['throttle:api', 'api_secret_check']], function () {
Route::post('api/v1/signup', [AccountController::class, 'store'])->name('signup.submit'); Route::post('api/v1/signup', [AccountController::class, 'store'])->name('signup.submit');
@ -272,7 +275,6 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
Route::post('recurring_expenses/bulk', [RecurringExpenseController::class, 'bulk'])->name('recurring_expenses.bulk'); Route::post('recurring_expenses/bulk', [RecurringExpenseController::class, 'bulk'])->name('recurring_expenses.bulk');
Route::put('recurring_expenses/{recurring_expense}/upload', [RecurringExpenseController::class, 'upload']); Route::put('recurring_expenses/{recurring_expense}/upload', [RecurringExpenseController::class, 'upload']);
Route::resource('recurring_invoices', RecurringInvoiceController::class); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::resource('recurring_invoices', RecurringInvoiceController::class); // name = (recurring_invoices. index / create / show / update / destroy / edit
Route::post('recurring_invoices/bulk', [RecurringInvoiceController::class, 'bulk'])->name('recurring_invoices.bulk'); Route::post('recurring_invoices/bulk', [RecurringInvoiceController::class, 'bulk'])->name('recurring_invoices.bulk');
Route::put('recurring_invoices/{recurring_invoice}/upload', [RecurringInvoiceController::class, 'upload']); Route::put('recurring_invoices/{recurring_invoice}/upload', [RecurringInvoiceController::class, 'upload']);
@ -291,6 +293,8 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
Route::post('reports/expenses', ExpenseReportController::class)->middleware('throttle:20,1'); Route::post('reports/expenses', ExpenseReportController::class)->middleware('throttle:20,1');
Route::post('reports/invoices', InvoiceReportController::class)->middleware('throttle:20,1'); Route::post('reports/invoices', InvoiceReportController::class)->middleware('throttle:20,1');
Route::post('reports/invoice_items', InvoiceItemReportController::class)->middleware('throttle:20,1'); Route::post('reports/invoice_items', InvoiceItemReportController::class)->middleware('throttle:20,1');
Route::post('reports/purchase_orders', PurchaseOrderReportController::class)->middleware('throttle:20,1');
Route::post('reports/purchase_order_items', PurchaseOrderItemReportController::class)->middleware('throttle:20,1');
Route::post('reports/quotes', QuoteReportController::class)->middleware('throttle:20,1'); Route::post('reports/quotes', QuoteReportController::class)->middleware('throttle:20,1');
Route::post('reports/quote_items', QuoteItemReportController::class)->middleware('throttle:20,1'); Route::post('reports/quote_items', QuoteItemReportController::class)->middleware('throttle:20,1');
Route::post('reports/recurring_invoices', RecurringInvoiceReportController::class)->middleware('throttle:20,1'); Route::post('reports/recurring_invoices', RecurringInvoiceReportController::class)->middleware('throttle:20,1');
@ -298,7 +302,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
Route::post('reports/products', ProductReportController::class)->middleware('throttle:20,1'); Route::post('reports/products', ProductReportController::class)->middleware('throttle:20,1');
Route::post('reports/product_sales', ProductSalesReportController::class)->middleware('throttle:20,1'); Route::post('reports/product_sales', ProductSalesReportController::class)->middleware('throttle:20,1');
Route::post('reports/tasks', TaskReportController::class)->middleware('throttle:20,1'); Route::post('reports/tasks', TaskReportController::class)->middleware('throttle:20,1');
Route::post('reports/vendors', VendorReportController::class)->middleware('throttle:20,1');
Route::post('reports/profitloss', ProfitAndLossController::class); Route::post('reports/profitloss', ProfitAndLossController::class);
Route::post('reports/ar_detail_report', ARDetailReportController::class); Route::post('reports/ar_detail_report', ARDetailReportController::class);
Route::post('reports/ar_summary_report', ARSummaryReportController::class); Route::post('reports/ar_summary_report', ARSummaryReportController::class);

View File

@ -18,18 +18,18 @@ use App\Http\Controllers\ClientPortal\SubscriptionController;
use App\Http\Controllers\Auth\ContactForgotPasswordController; use App\Http\Controllers\Auth\ContactForgotPasswordController;
use App\Http\Controllers\ClientPortal\PaymentMethodController; use App\Http\Controllers\ClientPortal\PaymentMethodController;
Route::get('client', [ContactLoginController::class, 'showLoginForm'])->name('client.catchall')->middleware(['domain_db', 'contact_account','locale']); //catch all Route::get('client', [ContactLoginController::class, 'showLoginForm'])->name('client.catchall')->middleware(['domain_db', 'contact_account','locale', 'throttle:portal']); //catch all
Route::get('client/login/{company_key?}', [ContactLoginController::class, 'showLoginForm'])->name('client.login')->middleware(['domain_db', 'contact_account','locale']); Route::get('client/login/{company_key?}', [ContactLoginController::class, 'showLoginForm'])->name('client.login')->middleware(['domain_db', 'contact_account','locale', 'throttle:portal']);
Route::post('client/login/{company_key?}', [ContactLoginController::class, 'login'])->name('client.login.submit'); Route::post('client/login/{company_key?}', [ContactLoginController::class, 'login'])->name('client.login.submit');
Route::get('client/register/{company_key?}', [ContactRegisterController::class, 'showRegisterForm'])->name('client.register')->middleware(['domain_db', 'contact_account', 'contact_register','locale']); Route::get('client/register/{company_key?}', [ContactRegisterController::class, 'showRegisterForm'])->name('client.register')->middleware(['domain_db', 'contact_account', 'contact_register','locale']);
Route::post('client/register/{company_key?}', [ContactRegisterController::class, 'register'])->middleware(['domain_db', 'contact_account', 'contact_register', 'locale', 'throttle:10,1']); Route::post('client/register/{company_key?}', [ContactRegisterController::class, 'register'])->middleware(['domain_db', 'contact_account', 'contact_register', 'locale', 'throttle:portal']);
Route::get('client/password/reset', [ContactForgotPasswordController::class, 'showLinkRequestForm'])->name('client.password.request')->middleware(['domain_db', 'contact_account','locale']); Route::get('client/password/reset', [ContactForgotPasswordController::class, 'showLinkRequestForm'])->name('client.password.request')->middleware(['domain_db', 'contact_account','locale', 'throttle:portal']);
Route::post('client/password/email', [ContactForgotPasswordController::class, 'sendResetLinkEmail'])->name('client.password.email')->middleware('locale'); Route::post('client/password/email', [ContactForgotPasswordController::class, 'sendResetLinkEmail'])->name('client.password.email')->middleware(['locale', 'throttle:portal']);
Route::get('client/password/reset/{token}', [ContactResetPasswordController::class, 'showResetForm'])->name('client.password.reset')->middleware(['domain_db', 'contact_account','locale']); Route::get('client/password/reset/{token}', [ContactResetPasswordController::class, 'showResetForm'])->name('client.password.reset')->middleware(['domain_db', 'contact_account','locale', 'throttle:portal']);
Route::post('client/password/reset', [ContactResetPasswordController::class, 'reset'])->name('client.password.update')->middleware(['domain_db', 'contact_account','locale']); Route::post('client/password/reset', [ContactResetPasswordController::class, 'reset'])->name('client.password.update')->middleware(['domain_db', 'contact_account','locale', 'throttle:portal']);
Route::get('view/{entity_type}/{invitation_key}', [App\Http\Controllers\ClientPortal\EntityViewController::class, 'index'])->name('client.entity_view'); Route::get('view/{entity_type}/{invitation_key}', [App\Http\Controllers\ClientPortal\EntityViewController::class, 'index'])->name('client.entity_view');
Route::get('view/{entity_type}/{invitation_key}/password', [App\Http\Controllers\ClientPortal\EntityViewController::class ,'password'])->name('client.entity_view.password'); Route::get('view/{entity_type}/{invitation_key}/password', [App\Http\Controllers\ClientPortal\EntityViewController::class ,'password'])->name('client.entity_view.password');
@ -51,6 +51,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'domain_db','check_clie
Route::get('plan', [App\Http\Controllers\ClientPortal\NinjaPlanController::class, 'plan'])->name('plan'); // name = (dashboard. index / create / show / update / destroy / edit Route::get('plan', [App\Http\Controllers\ClientPortal\NinjaPlanController::class, 'plan'])->name('plan'); // name = (dashboard. index / create / show / update / destroy / edit
Route::get('showBlob/{hash}', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'showBlob'])->name('invoices.showBlob');
Route::get('invoices', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'index'])->name('invoices.index')->middleware('portal_enabled'); Route::get('invoices', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'index'])->name('invoices.index')->middleware('portal_enabled');
Route::post('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'bulk'])->name('invoices.bulk'); Route::post('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'bulk'])->name('invoices.bulk');
Route::get('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'catch_bulk'])->name('invoices.catch_bulk'); Route::get('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'catch_bulk'])->name('invoices.catch_bulk');
@ -78,7 +79,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'domain_db','check_clie
Route::put('profile/{client_contact}/localization', [App\Http\Controllers\ClientPortal\ProfileController::class, 'updateClientLocalization'])->name('profile.edit_localization'); Route::put('profile/{client_contact}/localization', [App\Http\Controllers\ClientPortal\ProfileController::class, 'updateClientLocalization'])->name('profile.edit_localization');
Route::get('payment_methods/{payment_method}/verification', [App\Http\Controllers\ClientPortal\PaymentMethodController::class, 'verify'])->name('payment_methods.verification'); Route::get('payment_methods/{payment_method}/verification', [App\Http\Controllers\ClientPortal\PaymentMethodController::class, 'verify'])->name('payment_methods.verification');
Route::post('payment_methods/{payment_method}/verification', [App\Http\Controllers\ClientPortal\PaymentMethodController::class, 'processVerification'])->middleware(['throttle:10,1']); Route::post('payment_methods/{payment_method}/verification', [App\Http\Controllers\ClientPortal\PaymentMethodController::class, 'processVerification'])->middleware(['throttle:portal']);
Route::get('payment_methods/confirm', [App\Http\Controllers\ClientPortal\PaymentMethodController::class, 'store'])->name('payment_methods.confirm'); Route::get('payment_methods/confirm', [App\Http\Controllers\ClientPortal\PaymentMethodController::class, 'store'])->name('payment_methods.confirm');

View File

@ -25,10 +25,6 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'vendor', 'as' => 'vend
/*Invitation catches*/ /*Invitation catches*/
Route::get('purchase_order/{invitation_key}', [InvitationController::class, 'purchaseOrder']); Route::get('purchase_order/{invitation_key}', [InvitationController::class, 'purchaseOrder']);
Route::get('purchase_order/{invitation_key}/download', [InvitationController::class, 'download']); Route::get('purchase_order/{invitation_key}/download', [InvitationController::class, 'download']);
// Route::get('purchase_order/{invitation_key}/download_pdf', 'PurchaseOrderController@downloadPdf')->name('recurring_invoice.download_invitation_key');
// Route::get('purchase_order/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
}); });
Route::group(['middleware' => ['auth:vendor', 'vendor_locale', 'domain_db'], 'prefix' => 'vendor', 'as' => 'vendor.'], function () { Route::group(['middleware' => ['auth:vendor', 'vendor_locale', 'domain_db'], 'prefix' => 'vendor', 'as' => 'vendor.'], function () {
@ -37,6 +33,8 @@ Route::group(['middleware' => ['auth:vendor', 'vendor_locale', 'domain_db'], 'pr
Route::get('purchase_orders', [PurchaseOrderController::class, 'index'])->name('purchase_orders.index'); Route::get('purchase_orders', [PurchaseOrderController::class, 'index'])->name('purchase_orders.index');
Route::get('purchase_orders/{purchase_order}', [PurchaseOrderController::class, 'show'])->name('purchase_order.show'); Route::get('purchase_orders/{purchase_order}', [PurchaseOrderController::class, 'show'])->name('purchase_order.show');
Route::get('showBlob/{hash}', [PurchaseOrderController::class, 'showBlob'])->name('purchase_order.showBlob');
Route::get('profile/{vendor_contact}/edit', [VendorContactController::class, 'edit'])->name('profile.edit'); Route::get('profile/{vendor_contact}/edit', [VendorContactController::class, 'edit'])->name('profile.edit');
Route::put('profile/{vendor_contact}/edit', [VendorContactController::class, 'update'])->name('profile.update'); Route::put('profile/{vendor_contact}/edit', [VendorContactController::class, 'update'])->name('profile.update');

View File

@ -37,6 +37,54 @@ class ClientCsvTest extends TestCase
$this->withoutExceptionHandling(); $this->withoutExceptionHandling();
} }
public function testRecurringInvoiceExportCsv()
{
$data = [
'date_range' => 'this_year',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/recurring_invoices', $data);
$response->assertStatus(200);
}
public function testVendorExportCsv()
{
$data = [
'date_range' => 'this_year',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/vendors', $data);
$response->assertStatus(200);
}
public function testPurchaseOrderExportCsv()
{
$data = [
'date_range' => 'this_year',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/purchase_orders', $data);
$response->assertStatus(200);
}
public function testClientExportCsv() public function testClientExportCsv()
{ {
$data = [ $data = [

View File

@ -46,7 +46,7 @@ class ExportCompanyTest extends TestCase
public function testCompanyExport() public function testCompanyExport()
{ {
$res = (new CompanyExport($this->company, $this->company->users->first()))->handle(); $res = (new CompanyExport($this->company, $this->company->users->first(), '123'))->handle();
$this->assertTrue($res); $this->assertTrue($res);
} }

View File

@ -0,0 +1,259 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace Tests\Feature\Export;
use Tests\TestCase;
use App\Models\User;
use App\Models\Client;
use League\Csv\Reader;
use App\Models\Account;
use App\Models\Company;
use App\Models\Invoice;
use Tests\MockAccountData;
use App\Models\CompanyToken;
use App\Models\ClientContact;
use App\Utils\Traits\MakesHash;
use App\DataMapper\CompanySettings;
use App\Factory\CompanyUserFactory;
use App\Factory\InvoiceItemFactory;
use App\Services\Report\ARDetailReport;
use Illuminate\Routing\Middleware\ThrottleRequests;
/**
* @test
*/
class ReportCsvGenerationTest extends TestCase
{
use MakesHash;
public $faker;
protected function setUp() :void
{
parent::setUp();
$this->faker = \Faker\Factory::create();
$this->withoutMiddleware(
ThrottleRequests::class
);
$this->withoutExceptionHandling();
$this->buildData();
}
public $company;
public $user;
public $payload;
public $account;
public $client;
public $token;
public $cu;
/**
* start_date - Y-m-d
end_date - Y-m-d
date_range -
all
last7
last30
this_month
last_month
this_quarter
last_quarter
this_year
custom
is_income_billed - true = Invoiced || false = Payments
expense_billed - true = Expensed || false = Expenses marked as paid
include_tax - true tax_included || false - tax_excluded
*/
private function buildData()
{
$this->account = Account::factory()->create([
'hosted_client_count' => 1000,
'hosted_company_count' => 1000,
]);
$this->account->num_users = 3;
$this->account->save();
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
]);
$settings = CompanySettings::defaults();
$settings->client_online_payment_notification = false;
$settings->client_manual_payment_notification = false;
$this->company = Company::factory()->create([
'account_id' => $this->account->id,
'settings' => $settings,
]);
$this->company->settings = $settings;
$this->company->save();
$this->cu = CompanyUserFactory::create($this->user->id, $this->company->id, $this->account->id);
$this->cu->is_owner = true;
$this->cu->is_admin = true;
$this->cu->is_locked = false;
$this->cu->save();
$this->token = \Illuminate\Support\Str::random(64);
$company_token = new CompanyToken;
$company_token->user_id = $this->user->id;
$company_token->company_id = $this->company->id;
$company_token->account_id = $this->account->id;
$company_token->name = 'test token';
$company_token->token = $this->token;
$company_token->is_system = true;
$company_token->save();
$this->payload = [
'start_date' => '2000-01-01',
'end_date' => '2030-01-11',
'date_range' => 'custom',
'is_income_billed' => true,
'include_tax' => false,
];
$this->client = Client::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'is_deleted' => 0,
'name' => 'bob',
'address1' => '1234'
]);
ClientContact::factory()->create([
'user_id' => $this->user->id,
'client_id' => $this->client->id,
'company_id' => $this->company->id,
'is_primary' => 1,
'first_name' => 'john',
'last_name' => 'doe',
'email' => 'john@doe.com'
]);
}
public function testClientCsvGeneration()
{
$data = [
'date_range' => 'all',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/clients', $data);
$csv = $response->streamedContent();
$reader = Reader::createFromString($csv);
$reader->setHeaderOffset(0);
$res = $reader->fetchColumnByName('Street');
$res = iterator_to_array($res, true);
$this->assertEquals('1234', $res[1]);
$res = $reader->fetchColumnByName('Name');
$res = iterator_to_array($res, true);
$this->assertEquals('bob', $res[1]);
}
public function testClientContactCsvGeneration()
{
$data = [
'date_range' => 'all',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/contacts', $data);
$csv = $response->streamedContent();
$reader = Reader::createFromString($csv);
$reader->setHeaderOffset(0);
$res = $reader->fetchColumnByName('First Name');
$res = iterator_to_array($res, true);
$this->assertEquals('john', $res[1]);
$res = $reader->fetchColumnByName('Last Name');
$res = iterator_to_array($res, true);
$this->assertEquals('doe', $res[1]);
$res = $reader->fetchColumnByName('Email');
$res = iterator_to_array($res, true);
$this->assertEquals('john@doe.com', $res[1]);
}
public function testCreditCsvGeneration()
{
Credit::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
'amount' => 100,
'balance' => 50,
'status_id' => 2,
'discount' => 10,
'po_number' => '1234',
'public_notes' => 'Public',
'private_notes' => 'Private',
'terms' => 'Terms',
]);
$data = [
'date_range' => 'all',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/credits', $data);
}

View File

@ -0,0 +1,192 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace Tests\Feature;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithoutEvents;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
* @covers App\Http\Controllers\PaymentController
*/
class PaymentV2Test extends TestCase
{
use MakesHash;
use DatabaseTransactions;
use MockAccountData;
use WithoutEvents;
public $faker;
protected function setUp() :void
{
parent::setUp();
Session::start();
$this->faker = \Faker\Factory::create();
Model::reguard();
$this->makeTestData();
$this->withoutExceptionHandling();
$this->withoutMiddleware(
ThrottleRequests::class
);
}
public function testStorePaymentWithCreditsThenDeletingInvoices()
{
$client = Client::factory()->create(['company_id' =>$this->company->id, 'user_id' => $this->user->id, 'balance' => 20, 'paid_to_date' => 0]);
ClientContact::factory()->create([
'user_id' => $this->user->id,
'client_id' => $client->id,
'company_id' => $this->company->id,
'is_primary' => 1,
]);
$invoice = Invoice::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $client->id,
'status_id' => Invoice::STATUS_SENT,
'uses_inclusive_taxes' => false,
'amount' => 20,
'balance' => 20,
'discount' => 0,
'number' => uniqid("st", true),
'line_items' => []
]);
$this->assertEquals(20, $client->balance);
$this->assertEquals(0, $client->paid_to_date);
$this->assertEquals(20, $invoice->amount);
$this->assertEquals(20, $invoice->balance);
$credit = Credit::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'client_id' => $client->id,
'status_id' => Invoice::STATUS_SENT,
'uses_inclusive_taxes' => false,
'amount' => 20,
'balance' => 20,
'discount' => 0,
'number' => uniqid("st", true),
'line_items' => []
]);
$this->assertEquals(20, $credit->amount);
$this->assertEquals(20, $credit->balance);
$data = [
'client_id' => $client->hashed_id,
'invoices' => [
[
'invoice_id' => $invoice->hashed_id,
'amount' => 20,
],
],
'credits' => [
[
'credit_id' => $credit->hashed_id,
'amount' => 20,
],
],
'date' => '2020/12/12',
];
$response = null;
try {
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/payments?include=invoices', $data);
} catch (ValidationException $e) {
$message = json_decode($e->validator->getMessageBag(), 1);
nlog($message);
$this->assertNotNull($message);
}
$arr = $response->json();
$response->assertStatus(200);
$payment_id = $arr['data']['id'];
$payment = Payment::find($this->decodePrimaryKey($payment_id));
$this->assertNotNull($payment);
$this->assertNotNull($payment->invoices());
$this->assertEquals(1, $payment->invoices()->count());
$this->assertEquals(0, $payment->amount);
$this->assertEquals(0, $client->fresh()->balance);
$this->assertEquals(20, $client->fresh()->paid_to_date);
$data = [
'action' => 'delete',
'ids' => [
$invoice->hashed_id,
],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/invoices/bulk', $data);
$response->assertStatus(200);
$invoice = $invoice->fresh();
$payment = $payment->fresh();
$this->assertEquals(true, $invoice->is_deleted);
$this->assertEquals(0, $payment->amount);
$this->assertEquals(0, $client->fresh()->balance);
$this->assertEquals(0, $client->fresh()->paid_to_date);
$data = [
'action' => 'restore',
'ids' => [
$invoice->hashed_id,
],
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/invoices/bulk', $data);
$invoice = $invoice->fresh();
$this->assertEquals(false, $invoice->is_deleted);
$payment = $payment->fresh();
$this->assertEquals(0, $payment->amount);
$this->assertEquals(20, $client->fresh()->paid_to_date);
}
}

View File

@ -33,7 +33,7 @@ class PaymentTypeTest extends TestCase
$payment_type_class = new PaymentType; $payment_type_class = new PaymentType;
foreach($payment_type_class->type_names as $type) foreach($payment_type_class->type_names as $type)
{nlog($type); {
$this->assertTrue(Lang::has("texts.{$type}")); $this->assertTrue(Lang::has("texts.{$type}"));
} }
} }