mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge branches 'feature-brevo' and 'feature-brevo' of https://github.com/paulwer/invoiceninja; branch 'v5-develop' of https://github.com/invoiceninja/invoiceninja into feature-brevo
This commit is contained in:
commit
e443fb28ec
@ -1 +1 @@
|
||||
5.8.26
|
||||
5.8.37
|
@ -884,14 +884,14 @@ class CheckData extends Command
|
||||
public function checkClientSettings()
|
||||
{
|
||||
if ($this->option('fix') == 'true') {
|
||||
Client::query()->whereNull('country_id')->cursor()->each(function ($client) {
|
||||
Client::query()->whereNull('country_id')->orWhere('country_id', 0)->cursor()->each(function ($client) {
|
||||
$client->country_id = $client->company->settings->country_id;
|
||||
$client->saveQuietly();
|
||||
|
||||
$this->logMessage("Fixing country for # {$client->id}");
|
||||
});
|
||||
|
||||
Client::query()->whereNull("settings->currency_id")->cursor()->each(function ($client) {
|
||||
Client::query()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
|
||||
$settings = $client->settings;
|
||||
$settings->currency_id = (string)$client->company->settings->currency_id;
|
||||
$client->settings = $settings;
|
||||
@ -933,7 +933,6 @@ class CheckData extends Command
|
||||
|
||||
});
|
||||
|
||||
|
||||
Invoice::withTrashed()
|
||||
->where("partial", 0)
|
||||
->whereNotNull("partial_due_date")
|
||||
@ -947,7 +946,42 @@ class CheckData extends Command
|
||||
|
||||
});
|
||||
|
||||
Company::whereDoesntHave('company_users', function ($query){
|
||||
$query->where('is_owner', 1);
|
||||
})
|
||||
->cursor()
|
||||
->when(Ninja::isHosted())
|
||||
->each(function ($c){
|
||||
|
||||
$this->logMessage("Orphan Account # {$c->account_id}");
|
||||
|
||||
});
|
||||
|
||||
CompanyUser::whereDoesntHave('tokens')
|
||||
->cursor()
|
||||
->when(Ninja::isHosted())
|
||||
->each(function ($cu){
|
||||
|
||||
$this->logMessage("Missing tokens for Company User # {$cu->id}");
|
||||
|
||||
});
|
||||
|
||||
CompanyUser::whereDoesntHave('user')
|
||||
->cursor()
|
||||
->when(Ninja::isHosted())
|
||||
->each(function ($cu) {
|
||||
|
||||
$this->logMessage("Missing user for Company User # {$cu->id}");
|
||||
|
||||
});
|
||||
|
||||
$cus = CompanyUser::withTrashed()
|
||||
->whereHas("user", function ($query) {
|
||||
$query->whereColumn("users.account_id", "!=", "company_user.account_id");
|
||||
})->pluck('id')->implode(",");
|
||||
|
||||
|
||||
$this->logMessage("Cross Linked CompanyUser ids # {$cus}");
|
||||
|
||||
|
||||
}
|
||||
|
@ -264,14 +264,14 @@ class BaseRule implements RuleInterface
|
||||
return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->client->company->country()->iso_3166_2 == 'US' ? $this->client->company->tax_data->seller_subregion : 'CA';
|
||||
return 'CA';
|
||||
}
|
||||
}
|
||||
|
||||
public function isTaxableRegion(): bool
|
||||
{
|
||||
return $this->client->company->tax_data->regions->{$this->client_region}->tax_all_subregions ||
|
||||
(property_exists($this->client->company->tax_data->regions->{$this->client_region}->subregions, $this->client_subregion) && $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax);
|
||||
(property_exists($this->client->company->tax_data->regions->{$this->client_region}->subregions, $this->client_subregion) && ($this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax ?? false));
|
||||
}
|
||||
|
||||
public function defaultForeign(): self
|
||||
|
@ -36,4 +36,10 @@ interface RuleInterface
|
||||
public function override($item);
|
||||
|
||||
public function calculateRates();
|
||||
|
||||
public function regionWithNoTaxCoverage(string $iso_3166_2): bool;
|
||||
|
||||
public function setEntity($entity): self;
|
||||
|
||||
public function shouldCalcTax(): bool;
|
||||
}
|
||||
|
@ -55,14 +55,12 @@ class Handler extends ExceptionHandler
|
||||
|
||||
protected $selfHostDontReport = [
|
||||
FilePermissionsFailure::class,
|
||||
PDOException::class,
|
||||
MaxAttemptsExceededException::class,
|
||||
CommandNotFoundException::class,
|
||||
ValidationException::class,
|
||||
ModelNotFoundException::class,
|
||||
NotFoundHttpException::class,
|
||||
UnableToCreateDirectory::class,
|
||||
ConnectException::class,
|
||||
RuntimeException::class,
|
||||
InvalidArgumentException::class,
|
||||
CredentialsException::class,
|
||||
@ -140,7 +138,7 @@ class Handler extends ExceptionHandler
|
||||
'email' => 'anonymous@example.com',
|
||||
'name' => 'Anonymous User',
|
||||
]);
|
||||
} elseif (auth()->guard('user') && auth()->guard('user')->user() && auth()->user()->company() && auth()->user()->company()->account->report_errors) {
|
||||
} elseif (auth()->guard('user') && auth()->guard('user')->user() && auth()->user()->companyIsSet() && auth()->user()->company()->account->report_errors) {
|
||||
$scope->setUser([
|
||||
'id' => auth()->user()->account->key,
|
||||
'email' => 'anonymous@example.com',
|
||||
|
@ -826,8 +826,15 @@ class BaseExport
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
public function applyFilters(Builder $query): Builder
|
||||
|
||||
/**
|
||||
* Apply Product Filters
|
||||
*
|
||||
* @param Builder $query
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function applyProductFilters(Builder $query): Builder
|
||||
{
|
||||
|
||||
if(isset($this->input['product_key'])) {
|
||||
@ -844,8 +851,16 @@ class BaseExport
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addClientFilter($query, $clients): Builder
|
||||
|
||||
/**
|
||||
* Add Client Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param mixed $clients
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addClientFilter(Builder $query, $clients): Builder
|
||||
{
|
||||
if(is_string($clients)) {
|
||||
$clients = explode(',', $clients);
|
||||
@ -862,8 +877,16 @@ class BaseExport
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addVendorFilter($query, $vendors): Builder
|
||||
|
||||
/**
|
||||
* Add Vendor Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $vendors
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addVendorFilter(Builder$query, string $vendors): Builder
|
||||
{
|
||||
|
||||
if(is_string($vendors)) {
|
||||
@ -878,8 +901,16 @@ class BaseExport
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addProjectFilter($query, $projects): Builder
|
||||
|
||||
/**
|
||||
* AddProjectFilter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $projects
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addProjectFilter(Builder $query, string $projects): Builder
|
||||
{
|
||||
|
||||
if(is_string($projects)) {
|
||||
@ -894,8 +925,16 @@ class BaseExport
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addCategoryFilter($query, $expense_categories): Builder
|
||||
|
||||
/**
|
||||
* Add Category Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $expense_categories
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addCategoryFilter(Builder $query, string $expense_categories): Builder
|
||||
{
|
||||
|
||||
if(is_string($expense_categories)) {
|
||||
@ -911,13 +950,230 @@ class BaseExport
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addInvoiceStatusFilter($query, $status): Builder
|
||||
|
||||
/**
|
||||
* Add Payment Status Filters
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $status
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addPaymentStatusFilters(Builder $query, string $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if(in_array('all', $status_parameters)) {
|
||||
if(in_array('all', $status_parameters) || count($status_parameters) == 0) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->where(function ($query) use ($status_parameters) {
|
||||
$payment_filters = [];
|
||||
|
||||
if (in_array('pending', $status_parameters)) {
|
||||
$payment_filters[] = Payment::STATUS_PENDING;
|
||||
}
|
||||
|
||||
if (in_array('cancelled', $status_parameters)) {
|
||||
$payment_filters[] = Payment::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
if (in_array('failed', $status_parameters)) {
|
||||
$payment_filters[] = Payment::STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (in_array('completed', $status_parameters)) {
|
||||
$payment_filters[] = Payment::STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
if (in_array('partially_refunded', $status_parameters)) {
|
||||
$payment_filters[] = Payment::STATUS_PARTIALLY_REFUNDED;
|
||||
}
|
||||
|
||||
if (in_array('refunded', $status_parameters)) {
|
||||
$payment_filters[] = Payment::STATUS_REFUNDED;
|
||||
}
|
||||
|
||||
if (count($payment_filters) > 0) {
|
||||
$query->whereIn('status_id', $payment_filters);
|
||||
}
|
||||
|
||||
if(in_array('partially_unapplied', $status_parameters)) {
|
||||
$query->whereColumn('amount', '>', 'applied')->where('refunded', 0);
|
||||
}
|
||||
});
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add RecurringInvoice Status Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $status
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addRecurringInvoiceStatusFilter(Builder $query, string $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if (in_array('all', $status_parameters) || count($status_parameters) == 0){
|
||||
return $query;
|
||||
}
|
||||
|
||||
$recurring_filters = [];
|
||||
|
||||
if (in_array('active', $status_parameters)) {
|
||||
$recurring_filters[] = RecurringInvoice::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
if (in_array('paused', $status_parameters)) {
|
||||
$recurring_filters[] = RecurringInvoice::STATUS_PAUSED;
|
||||
}
|
||||
|
||||
if (in_array('completed', $status_parameters)) {
|
||||
$recurring_filters[] = RecurringInvoice::STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
if (count($recurring_filters) >= 1) {
|
||||
return $query->whereIn('status_id', $recurring_filters);
|
||||
}
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
/**
|
||||
* Add QuoteStatus Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $status
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addQuoteStatusFilter(Builder $query, string $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if (in_array('all', $status_parameters)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->where(function ($query) use ($status_parameters) {
|
||||
if (in_array('sent', $status_parameters)) {
|
||||
$query->orWhere(function ($q) {
|
||||
$q->where('status_id', Quote::STATUS_SENT)
|
||||
->whereNull('due_date')
|
||||
->orWhere('due_date', '>=', now()->toDateString());
|
||||
});
|
||||
}
|
||||
|
||||
$quote_filters = [];
|
||||
|
||||
if (in_array('draft', $status_parameters)) {
|
||||
$quote_filters[] = Quote::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
if (in_array('approved', $status_parameters)) {
|
||||
$quote_filters[] = Quote::STATUS_APPROVED;
|
||||
}
|
||||
|
||||
if (count($quote_filters) > 0) {
|
||||
$query->orWhereIn('status_id', $quote_filters);
|
||||
}
|
||||
|
||||
if (in_array('expired', $status_parameters)) {
|
||||
$query->orWhere(function ($q) {
|
||||
$q->where('status_id', Quote::STATUS_SENT)
|
||||
->whereNotNull('due_date')
|
||||
->where('due_date', '<=', now()->toDateString());
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array('upcoming', $status_parameters)) {
|
||||
$query->orWhere(function ($q) {
|
||||
$q->where('status_id', Quote::STATUS_SENT)
|
||||
->where('due_date', '>=', now()->toDateString())
|
||||
->orderBy('due_date', 'DESC');
|
||||
});
|
||||
}
|
||||
|
||||
if(in_array('converted', $status_parameters)) {
|
||||
$query->orWhere(function ($q) {
|
||||
$q->whereNotNull('invoice_id');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add PurchaseOrder Status Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $status
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addPurchaseOrderStatusFilter(Builder $query, string $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if (in_array('all', $status_parameters) || count($status_parameters) == 0) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->where(function ($query) use ($status_parameters) {
|
||||
$po_status = [];
|
||||
|
||||
if (in_array('draft', $status_parameters)) {
|
||||
$po_status[] = PurchaseOrder::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
if (in_array('sent', $status_parameters)) {
|
||||
$query->orWhere(function ($q) {
|
||||
$q->where('status_id', PurchaseOrder::STATUS_SENT)
|
||||
->whereNull('due_date')
|
||||
->orWhere('due_date', '>=', now()->toDateString());
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array('accepted', $status_parameters)) {
|
||||
$po_status[] = PurchaseOrder::STATUS_ACCEPTED;
|
||||
}
|
||||
|
||||
if (in_array('cancelled', $status_parameters)) {
|
||||
$po_status[] = PurchaseOrder::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
if (count($po_status) >= 1) {
|
||||
$query->whereIn('status_id', $po_status);
|
||||
}
|
||||
});
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Invoice Status Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $status
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addInvoiceStatusFilter(Builder $query, string $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if(in_array('all', $status_parameters) || count($status_parameters) == 0) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
@ -942,6 +1198,10 @@ class BaseExport
|
||||
$invoice_filters[] = Invoice::STATUS_PARTIAL;
|
||||
}
|
||||
|
||||
if (in_array('cancelled', $status_parameters)) {
|
||||
$invoice_filters[] = Invoice::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
if (count($invoice_filters) > 0) {
|
||||
$nested->whereIn('status_id', $invoice_filters);
|
||||
}
|
||||
@ -965,15 +1225,19 @@ class BaseExport
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addDateRange($query)
|
||||
|
||||
/**
|
||||
* Add Date Range
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addDateRange(Builder $query): Builder
|
||||
{
|
||||
$query = $this->applyFilters($query);
|
||||
$query = $this->applyProductFilters($query);
|
||||
|
||||
$date_range = $this->input['date_range'];
|
||||
|
||||
nlog($date_range);
|
||||
|
||||
if (array_key_exists('date_key', $this->input) && strlen($this->input['date_key']) > 1) {
|
||||
$this->date_key = $this->input['date_key'];
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class ClientExport extends BaseExport
|
||||
$query = Client::query()->with('contacts')
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
|
@ -103,10 +103,14 @@ class CreditExport extends BaseExport
|
||||
->withTrashed()
|
||||
->with('client')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
if($this->input['status'] ?? false) {
|
||||
$query = $this->addCreditStatusFilter($query, $this->input['status']);
|
||||
}
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
}
|
||||
@ -162,6 +166,40 @@ class CreditExport extends BaseExport
|
||||
return $this->decorateAdvancedFields($credit, $entity);
|
||||
}
|
||||
|
||||
public function addCreditStatusFilter($query, $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if (in_array('all', $status_parameters)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$credit_filters = [];
|
||||
|
||||
if (in_array('draft', $status_parameters)) {
|
||||
$credit_filters[] = Credit::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
if (in_array('sent', $status_parameters)) {
|
||||
$credit_filters[] = Credit::STATUS_SENT;
|
||||
}
|
||||
|
||||
if (in_array('partial', $status_parameters)) {
|
||||
$credit_filters[] = Credit::STATUS_PARTIAL;
|
||||
}
|
||||
|
||||
if (in_array('applied', $status_parameters)) {
|
||||
$credit_filters[] = Credit::STATUS_APPLIED;
|
||||
}
|
||||
|
||||
if (count($credit_filters) >= 1) {
|
||||
$query->whereIn('status_id', $credit_filters);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function decorateAdvancedFields(Credit $credit, array $entity): array
|
||||
{
|
||||
// if (in_array('country_id', $this->input['report_keys'])) {
|
||||
|
@ -83,10 +83,14 @@ class ExpenseExport extends BaseExport
|
||||
->with('client')
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
if($this->input['status'] ?? false) {
|
||||
$query = $this->addExpenseStatusFilter($query, $this->input['status']);
|
||||
}
|
||||
|
||||
if(isset($this->input['clients'])) {
|
||||
$query = $this->addClientFilter($query, $this->input['clients']);
|
||||
}
|
||||
@ -152,6 +156,55 @@ class ExpenseExport extends BaseExport
|
||||
return $this->decorateAdvancedFields($expense, $entity);
|
||||
}
|
||||
|
||||
protected function addExpenseStatusFilter($query, $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if (in_array('all', $status_parameters)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->where(function ($query) use ($status_parameters) {
|
||||
if (in_array('logged', $status_parameters)) {
|
||||
$query->orWhere(function ($query) {
|
||||
$query->where('amount', '>', 0)
|
||||
->whereNull('invoice_id')
|
||||
->whereNull('payment_date')
|
||||
->where('should_be_invoiced', false);
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array('pending', $status_parameters)) {
|
||||
$query->orWhere(function ($query) {
|
||||
$query->where('should_be_invoiced', true)
|
||||
->whereNull('invoice_id');
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array('invoiced', $status_parameters)) {
|
||||
$query->orWhere(function ($query) {
|
||||
$query->whereNotNull('invoice_id');
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array('paid', $status_parameters)) {
|
||||
$query->orWhere(function ($query) {
|
||||
$query->whereNotNull('payment_date');
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array('unpaid', $status_parameters)) {
|
||||
$query->orWhere(function ($query) {
|
||||
$query->whereNull('payment_date');
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function decorateAdvancedFields(Expense $expense, array $entity): array
|
||||
{
|
||||
// if (in_array('expense.currency_id', $this->input['report_keys'])) {
|
||||
|
@ -58,7 +58,7 @@ class InvoiceExport extends BaseExport
|
||||
->withTrashed()
|
||||
->with('client')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
@ -151,9 +151,9 @@ class InvoiceExport extends BaseExport
|
||||
// $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
|
||||
// }
|
||||
|
||||
// if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
|
||||
// $entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
|
||||
// }
|
||||
if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
|
||||
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
|
||||
}
|
||||
|
||||
if (in_array('invoice.auto_bill_enabled', $this->input['report_keys'])) {
|
||||
$entity['invoice.auto_bill_enabled'] = $invoice->auto_bill_enabled ? ctrans('texts.yes') : ctrans('texts.no');
|
||||
|
@ -71,11 +71,15 @@ class InvoiceItemExport extends BaseExport
|
||||
->withTrashed()
|
||||
->with('client')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->applyFilters($query);
|
||||
if($this->input['status'] ?? false) {
|
||||
$query = $this->addInvoiceStatusFilter($query, $this->input['status']);
|
||||
}
|
||||
|
||||
$query = $this->applyProductFilters($query);
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
@ -232,9 +236,9 @@ class InvoiceItemExport extends BaseExport
|
||||
// $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
|
||||
// }
|
||||
|
||||
// if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
|
||||
// $entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
|
||||
// }
|
||||
if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
|
||||
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
|
||||
}
|
||||
|
||||
if (in_array('invoice.assigned_user_id', $this->input['report_keys'])) {
|
||||
$entity['invoice.assigned_user_id'] = $invoice->assigned_user ? $invoice->assigned_user->present()->name() : '';
|
||||
|
@ -61,6 +61,8 @@ class PaymentExport extends BaseExport
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->addPaymentStatusFilters($query, $this->input['status'] ?? '');
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
}
|
||||
|
@ -31,51 +31,6 @@ class PurchaseOrderExport extends BaseExport
|
||||
|
||||
private Decorator $decorator;
|
||||
|
||||
public array $entity_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',
|
||||
'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',
|
||||
];
|
||||
|
||||
private array $decorate_keys = [
|
||||
'country',
|
||||
'currency_id',
|
||||
'status',
|
||||
'vendor',
|
||||
'project',
|
||||
];
|
||||
|
||||
|
||||
public function __construct(Company $company, array $input)
|
||||
{
|
||||
$this->company = $company;
|
||||
@ -104,10 +59,12 @@ class PurchaseOrderExport extends BaseExport
|
||||
->withTrashed()
|
||||
->with('vendor')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->addPurchaseOrderStatusFilter($query, $this->input['status'] ?? '');
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
}
|
||||
@ -167,7 +124,7 @@ class PurchaseOrderExport extends BaseExport
|
||||
if (is_array($parts) && $parts[0] == 'purchase_order' && array_key_exists($parts[1], $transformed_purchase_order)) {
|
||||
$entity[$key] = $transformed_purchase_order[$parts[1]];
|
||||
} else {
|
||||
// nlog($key);
|
||||
nlog($key);
|
||||
$entity[$key] = $this->decorator->transform($key, $purchase_order);
|
||||
// $entity[$key] = '';
|
||||
|
||||
@ -182,16 +139,13 @@ class PurchaseOrderExport extends BaseExport
|
||||
|
||||
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('purchase_order.currency_id', $this->input['report_keys'])) {
|
||||
$entity['purchase_order.currency_id'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code;
|
||||
}
|
||||
|
||||
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('purchase_order.vendor_id', $this->input['report_keys'])) {
|
||||
$entity['purchase_order.vendor_id'] = $purchase_order->vendor->present()->name();
|
||||
}
|
||||
|
||||
if (in_array('purchase_order.status', $this->input['report_keys'])) {
|
||||
|
@ -63,10 +63,12 @@ class PurchaseOrderItemExport extends BaseExport
|
||||
$query = PurchaseOrder::query()
|
||||
->withTrashed()
|
||||
->with('vendor')->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->addPurchaseOrderStatusFilter($query, $this->input['status'] ?? '');
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
}
|
||||
@ -190,23 +192,35 @@ class PurchaseOrderItemExport extends BaseExport
|
||||
|
||||
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 (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);
|
||||
// }
|
||||
|
||||
if (in_array('purchase_order.currency_id', $this->input['report_keys'])) {
|
||||
$entity['purchase_order.currency_id'] = $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 (in_array('purchase_order.vendor_id', $this->input['report_keys'])) {
|
||||
$entity['purchase_order.vendor_id'] = $purchase_order->vendor->present()->name();
|
||||
}
|
||||
|
||||
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);
|
||||
if (in_array('purchase_order.status', $this->input['report_keys'])) {
|
||||
$entity['purchase_order.status'] = $purchase_order->stringStatus($purchase_order->status_id);
|
||||
}
|
||||
|
||||
if (in_array('purchase_order.user_id', $this->input['report_keys'])) {
|
||||
|
@ -65,10 +65,12 @@ class QuoteExport extends BaseExport
|
||||
->withTrashed()
|
||||
->with('client')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->addQuoteStatusFilter($query, $this->input['status'] ?? '');
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
}
|
||||
|
@ -66,10 +66,12 @@ class QuoteItemExport extends BaseExport
|
||||
$query = Quote::query()
|
||||
->withTrashed()
|
||||
->with('client')->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->addQuoteStatusFilter($query, $this->input['status'] ?? '');
|
||||
|
||||
if($this->input['document_email_attachment'] ?? false) {
|
||||
$this->queueDocuments($query);
|
||||
}
|
||||
|
@ -57,10 +57,12 @@ class RecurringInvoiceExport extends BaseExport
|
||||
->withTrashed()
|
||||
->with('client')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
$query = $this->addRecurringInvoiceStatusFilter($query, $this->input['status'] ?? '');
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class TaskExport extends BaseExport
|
||||
$query = Task::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
@ -202,6 +202,34 @@ class TaskExport extends BaseExport
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Task Status Filter
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $status
|
||||
* @return Builder
|
||||
*/
|
||||
protected function addTaskStatusFilter(Builder $query, string $status): Builder
|
||||
{
|
||||
|
||||
$status_parameters = explode(',', $status);
|
||||
|
||||
if (in_array('all', $status_parameters) || count($status_parameters) == 0) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
if (in_array('invoiced', $status_parameters)) {
|
||||
$query->whereNotNull('invoice_id');
|
||||
}
|
||||
|
||||
if (in_array('uninvoiced', $status_parameters)) {
|
||||
$query->whereNull('invoice_id');
|
||||
}
|
||||
|
||||
return $query;
|
||||
|
||||
}
|
||||
|
||||
private function decorateAdvancedFields(Task $task, array $entity): array
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ class VendorExport extends BaseExport
|
||||
$query = Vendor::query()->with('contacts')
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
->where('is_deleted', $this->input['include_deleted'] ?? false);
|
||||
|
||||
$query = $this->addDateRange($query);
|
||||
|
||||
|
@ -79,7 +79,6 @@ class InvoiceDecorator extends Decorator implements DecoratorInterface
|
||||
return $invoice->partial_due_date ?? '';
|
||||
}
|
||||
|
||||
|
||||
public function assigned_user_id(Invoice $invoice)
|
||||
{
|
||||
return $invoice->assigned_user ? $invoice->assigned_user->present()->name() : '';
|
||||
|
@ -29,11 +29,13 @@ class DocumentFilters extends QueryFilters
|
||||
*/
|
||||
public function filter(string $filter = ''): Builder
|
||||
{
|
||||
|
||||
if (strlen($filter) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
return $this->builder->where('name', 'like', '%'.$filter.'%');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,9 +49,46 @@ class DocumentFilters extends QueryFilters
|
||||
*/
|
||||
public function client_id(string $client_id = ''): Builder
|
||||
{
|
||||
return $this->builder;
|
||||
|
||||
return $this->builder->where(function ($query) use ($client_id) {
|
||||
$query->whereHasMorph('documentable', [
|
||||
\App\Models\Invoice::class,
|
||||
\App\Models\Quote::class,
|
||||
\App\Models\Credit::class,
|
||||
\App\Models\Expense::class,
|
||||
\App\Models\Payment::class,
|
||||
\App\Models\Task::class,
|
||||
\App\Models\RecurringExpense::class,
|
||||
\App\Models\RecurringInvoice::class,
|
||||
\App\Models\Project::class,
|
||||
], function ($q2) use ($client_id) {
|
||||
$q2->where('client_id', $this->decodePrimaryKey($client_id));
|
||||
})->orWhereHasMorph('documentable', [\App\Models\Client::class], function ($q3) use ($client_id) {
|
||||
$q3->where('id', $this->decodePrimaryKey($client_id));
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public function type(string $types = '')
|
||||
{
|
||||
$types = explode(',', $types);
|
||||
|
||||
foreach ($types as $type)
|
||||
{
|
||||
match($type) {
|
||||
'private' => $this->builder->where('is_public', 0),
|
||||
'public' => $this->builder->where('is_public', 1),
|
||||
'pdf' => $this->builder->where('type', 'pdf'),
|
||||
'image' => $this->builder->whereIn('type', ['png','jpeg','jpg','gif','svg']),
|
||||
'other' => $this->builder->whereNotIn('type', ['pdf','png','jpeg','jpg','gif','svg']),
|
||||
default => $this->builder,
|
||||
};
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the list based on $sort.
|
||||
*
|
||||
|
@ -62,6 +62,10 @@ class InvoiceFilters extends QueryFilters
|
||||
$invoice_filters[] = Invoice::STATUS_PAID;
|
||||
}
|
||||
|
||||
if (in_array('cancelled', $status_parameters)) {
|
||||
$invoice_filters[] = Invoice::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
if (in_array('unpaid', $status_parameters)) {
|
||||
$invoice_filters[] = Invoice::STATUS_SENT;
|
||||
$invoice_filters[] = Invoice::STATUS_PARTIAL;
|
||||
@ -324,6 +328,7 @@ class InvoiceFilters extends QueryFilters
|
||||
}
|
||||
|
||||
if($sort_col[0] == 'number') {
|
||||
// return $this->builder->orderByRaw('CAST(number AS UNSIGNED), number ' . $dir);
|
||||
return $this->builder->orderByRaw('ABS(number) ' . $dir);
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ class PaymentFilters extends QueryFilters
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col, Schema::getColumnListing('payments'))) {
|
||||
if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col[0], Schema::getColumnListing('payments'))) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ class UserFilters extends QueryFilters
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
if (!is_array($sort_col) || count($sort_col) != 2) {
|
||||
if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col[0], \Illuminate\Support\Facades\Schema::getColumnListing('users'))) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
namespace App\Helpers\Bank\Nordigen;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Services\Email\Email;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Services\Email\EmailObject;
|
||||
@ -96,11 +97,11 @@ class Nordigen
|
||||
return $it->transform($out);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
if (strpos($e->getMessage(), "Invalid Account ID") !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
nlog("Nordigen getAccount() failed => {$account_id} => " . $e->getMessage());
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,11 +139,11 @@ class Nordigen
|
||||
* @param string $dateFrom
|
||||
* @return array
|
||||
*/
|
||||
public function getTransactions(string $accountId, string $dateFrom = null): array
|
||||
public function getTransactions(Company $company, string $accountId, string $dateFrom = null): array
|
||||
{
|
||||
$transactionResponse = $this->client->account($accountId)->getAccountTransactions($dateFrom);
|
||||
|
||||
$it = new TransactionTransformer();
|
||||
$it = new TransactionTransformer($company);
|
||||
return $it->transform($transactionResponse);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,10 @@
|
||||
namespace App\Helpers\Bank\Nordigen\Transformer;
|
||||
|
||||
use App\Helpers\Bank\BankRevenueInterface;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\Company;
|
||||
use App\Models\DateFormat;
|
||||
use App\Models\Timezone;
|
||||
use Carbon\Carbon;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Log;
|
||||
@ -66,6 +69,13 @@ class TransactionTransformer implements BankRevenueInterface
|
||||
{
|
||||
use AppSetup;
|
||||
|
||||
private Company $company;
|
||||
|
||||
function __construct(Company $company)
|
||||
{
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
public function transform($transactionResponse)
|
||||
{
|
||||
$data = [];
|
||||
@ -112,8 +122,8 @@ class TransactionTransformer implements BankRevenueInterface
|
||||
// enrich description with currencyExchange informations
|
||||
if (isset($transaction['currencyExchange'])) {
|
||||
foreach ($transaction["currencyExchange"] as $exchangeRate) {
|
||||
$targetAmount = round($amount * (float) ($exchangeRate["exchangeRate"] ?? 1) , 2);
|
||||
$description .= '\nexchangeRate: ' . $amount . " " . ($exchangeRate["sourceCurrency"] ?? '?') . " = " . $targetAmount . " " . ($exchangeRate["targetCurrency"] ?? '?') . " (" . ($exchangeRate["quotationDate"] ?? '?') . ")";
|
||||
$targetAmount = round($amount * (float) ($exchangeRate["exchangeRate"] ?? 1), 2);
|
||||
$description .= '\n' . ctrans('texts.exchange_rate') . ' : ' . $amount . " " . ($exchangeRate["sourceCurrency"] ?? '?') . " = " . $targetAmount . " " . ($exchangeRate["targetCurrency"] ?? '?') . " (" . (isset($exchangeRate["quotationDate"]) ? $this->formatDate($exchangeRate["quotationDate"]) : '?') . ")";
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,4 +174,24 @@ class TransactionTransformer implements BankRevenueInterface
|
||||
|
||||
}
|
||||
|
||||
private function formatDate(string $input)
|
||||
{
|
||||
$timezone = Timezone::find($this->company->settings->timezone_id);
|
||||
$timezone_name = 'US/Eastern';
|
||||
|
||||
if ($timezone) {
|
||||
$timezone_name = $timezone->name;
|
||||
}
|
||||
|
||||
$date_format_default = 'Y-m-d';
|
||||
|
||||
$date_format = DateFormat::find($this->company->settings->date_format_id);
|
||||
|
||||
if ($date_format) {
|
||||
$date_format_default = $date_format->format;
|
||||
}
|
||||
|
||||
return Carbon::createFromFormat("d-m-Y", $input)->setTimezone($timezone_name)->format($date_format_default) ?? $input;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -177,10 +177,12 @@ class InvoiceItemSum
|
||||
|
||||
if (in_array($this->client->company->country()->iso_3166_2, $this->tax_jurisdictions)) { //only calculate for supported tax jurisdictions
|
||||
|
||||
|
||||
/** @var \App\DataMapper\Tax\BaseRule $class */
|
||||
$class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule";
|
||||
|
||||
$this->rule = new $class();
|
||||
|
||||
|
||||
if($this->rule->regionWithNoTaxCoverage($this->client->country->iso_3166_2)) {
|
||||
return $this;
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class InvoiceSum
|
||||
|
||||
private function calculateInvoiceTaxes(): self
|
||||
{
|
||||
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
|
||||
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) >= 2) {
|
||||
$tax = $this->taxer($this->total, $this->invoice->tax_rate1);
|
||||
$tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name1, $this->invoice->tax_rate1);
|
||||
|
||||
@ -130,7 +130,7 @@ class InvoiceSum
|
||||
$this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax];
|
||||
}
|
||||
|
||||
if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 1) {
|
||||
if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) >= 2) {
|
||||
$tax = $this->taxer($this->total, $this->invoice->tax_rate2);
|
||||
$tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name2, $this->invoice->tax_rate2);
|
||||
|
||||
@ -138,7 +138,7 @@ class InvoiceSum
|
||||
$this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax];
|
||||
}
|
||||
|
||||
if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 1) {
|
||||
if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) >= 2) {
|
||||
$tax = $this->taxer($this->total, $this->invoice->tax_rate3);
|
||||
$tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name3, $this->invoice->tax_rate3);
|
||||
|
||||
@ -242,9 +242,9 @@ class InvoiceSum
|
||||
|
||||
if ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
|
||||
if ($this->invoice->amount != $this->invoice->balance) {
|
||||
$paid_to_date = $this->invoice->amount - $this->invoice->balance;
|
||||
// $paid_to_date = $this->invoice->amount - $this->invoice->balance;
|
||||
|
||||
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $paid_to_date;
|
||||
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; //21-02-2024 cannot use the calculated $paid_to_date here as it could send the balance backward.
|
||||
} else {
|
||||
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision);
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class InvoiceSumInclusive
|
||||
private function calculateInvoiceTaxes()
|
||||
{
|
||||
$amount = $this->total;
|
||||
|
||||
|
||||
if ($this->invoice->discount > 0 && $this->invoice->is_amount_discount) {
|
||||
$amount = $this->formatValue(($this->sub_total - $this->invoice->discount), 2);
|
||||
}
|
||||
@ -131,20 +131,20 @@ class InvoiceSumInclusive
|
||||
$amount = $this->formatValue(($this->sub_total - ($this->sub_total * ($this->invoice->discount / 100))), 2);
|
||||
}
|
||||
|
||||
if ($this->invoice->tax_rate1 > 0) {
|
||||
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
|
||||
$tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount);
|
||||
$this->total_taxes += $tax;
|
||||
|
||||
$this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax];
|
||||
}
|
||||
|
||||
if ($this->invoice->tax_rate2 > 0) {
|
||||
if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 1) {
|
||||
$tax = $this->calcInclusiveLineTax($this->invoice->tax_rate2, $amount);
|
||||
$this->total_taxes += $tax;
|
||||
$this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax];
|
||||
}
|
||||
|
||||
if ($this->invoice->tax_rate3 > 0) {
|
||||
if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 1) {
|
||||
$tax = $this->calcInclusiveLineTax($this->invoice->tax_rate3, $amount);
|
||||
$this->total_taxes += $tax;
|
||||
$this->total_tax_map[] = ['name' => $this->invoice->tax_name3.' '.floatval($this->invoice->tax_rate3).'%', 'total' => $tax];
|
||||
@ -259,9 +259,9 @@ class InvoiceSumInclusive
|
||||
/* If amount != balance then some money has been paid on the invoice, need to subtract this difference from the total to set the new balance */
|
||||
if ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
|
||||
if ($this->invoice->amount != $this->invoice->balance) {
|
||||
$paid_to_date = $this->invoice->amount - $this->invoice->balance;
|
||||
// $paid_to_date = $this->invoice->amount - $this->invoice->balance;
|
||||
|
||||
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision) - $paid_to_date;
|
||||
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date;
|
||||
} else {
|
||||
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision);
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ class SwissQrGenerator
|
||||
// Optionally, add some human-readable information about what the bill is for.
|
||||
$qrBill->setAdditionalInformation(
|
||||
QrBill\DataGroup\Element\AdditionalInformation::create(
|
||||
$this->invoice->public_notes ? substr($this->invoice->public_notes, 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number])
|
||||
$this->invoice->public_notes ? substr(strip_tags($this->invoice->public_notes), 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number])
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -49,6 +49,9 @@ use App\Http\Requests\Client\ClientDocumentsRequest;
|
||||
use App\Http\Requests\Client\ReactivateClientEmailRequest;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Project;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Transformers\DocumentTransformer;
|
||||
|
||||
@ -421,7 +424,7 @@ class ClientController extends BaseController
|
||||
|
||||
$documents = Document::query()
|
||||
->company()
|
||||
->whereHasMorph('documentable', [Invoice::class, Quote::class, Credit::class, Expense::class, Payment::class, Task::class], function ($query) use ($client) {
|
||||
->whereHasMorph('documentable', [Invoice::class, Quote::class, Credit::class, Expense::class, Payment::class, Task::class, RecurringInvoice::class, RecurringExpense::class, Project::class], function ($query) use ($client) {
|
||||
$query->where('client_id', $client->id);
|
||||
})
|
||||
->orWhereHasMorph('documentable', [Client::class], function ($query) use ($client) {
|
||||
|
@ -45,7 +45,7 @@ class EmailPreferencesController extends Controller
|
||||
|
||||
if ($invitation->contact->is_locked && !Cache::has("unsubscribe_notitfication_suppression:{$invitation_key}")) {
|
||||
$nmo = new NinjaMailerObject();
|
||||
$nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($invitation->contact, $invitation->contact->company, $invitation->contact->company->owner()->company_users()->first()->portalType() ?? true))->build());
|
||||
$nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($invitation->contact, $invitation->contact->company, true))->build());
|
||||
$nmo->company = $invitation->contact->company;
|
||||
$nmo->to_user = $invitation->contact->company->owner();
|
||||
$nmo->settings = $invitation->contact->company->settings;
|
||||
|
@ -271,6 +271,7 @@ class InvitationController extends Controller
|
||||
->with('contact.client')
|
||||
->firstOrFail();
|
||||
|
||||
|
||||
if ($invitation->contact->trashed()) {
|
||||
$invitation->contact->restore();
|
||||
}
|
||||
@ -294,7 +295,10 @@ class InvitationController extends Controller
|
||||
'payable_invoices' => [
|
||||
['invoice_id' => $invitation->invoice->hashed_id, 'amount' => $amount],
|
||||
],
|
||||
'signature' => false
|
||||
'signature' => false,
|
||||
'contact_first_name' => $invitation->contact->first_name ?? '',
|
||||
'contact_last_name' => $invitation->contact->last_name ?? '',
|
||||
'contact_email' => $invitation->contact->email ?? ''
|
||||
];
|
||||
|
||||
$request->replace($data);
|
||||
|
@ -236,7 +236,6 @@ class InvoiceController extends Controller
|
||||
'hashed_ids' => $invoices->pluck('hashed_id'),
|
||||
'total' => $total,
|
||||
'variables' => $variables,
|
||||
|
||||
];
|
||||
|
||||
return $this->render('invoices.payment', $data);
|
||||
|
@ -12,16 +12,17 @@
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Utils\Number;
|
||||
use App\Utils\HtmlEngine;
|
||||
use Illuminate\View\View;
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\PrePayments\StorePrePaymentRequest;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\View\View;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Http\Requests\ClientPortal\PrePayments\StorePrePaymentRequest;
|
||||
|
||||
/**
|
||||
* Class PrePaymentController.
|
||||
@ -88,6 +89,8 @@ class PrePaymentController extends Controller
|
||||
|
||||
$total = $invoice->balance;
|
||||
|
||||
$invitation = $invoice->invitations->first();
|
||||
|
||||
//format totals
|
||||
$formatted_total = Number::formatMoney($invoice->amount, auth()->guard('contact')->user()->client);
|
||||
|
||||
@ -113,6 +116,8 @@ class PrePaymentController extends Controller
|
||||
'frequency_id' => $request->frequency_id,
|
||||
'remaining_cycles' => $request->remaining_cycles,
|
||||
'is_recurring' => $request->is_recurring == 'on' ? true : false,
|
||||
'variables' => $variables = ($invitation && auth()->guard('contact')->user()->client->getSetting('show_accept_invoice_terms')) ? (new HtmlEngine($invitation))->generateLabelsAndValues() : false,
|
||||
|
||||
];
|
||||
|
||||
return $this->render('invoices.payment', $data);
|
||||
|
@ -25,6 +25,10 @@ class SubscriptionPurchaseController extends Controller
|
||||
{
|
||||
App::setLocale($subscription->company->locale());
|
||||
|
||||
if ($subscription->trashed()) {
|
||||
return $this->render('generic.not_available', ['account' => $subscription->company->account, 'company' => $subscription->company]);
|
||||
}
|
||||
|
||||
/* Make sure the contact is logged into the correct company for this subscription */
|
||||
if (auth()->guard('contact')->user() && auth()->guard('contact')->user()->company_id != $subscription->company_id) {
|
||||
auth()->guard('contact')->logout();
|
||||
|
@ -260,6 +260,8 @@ class ImportController extends Controller
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line **/
|
||||
return $bestDelimiter ?? ',';
|
||||
|
||||
}
|
||||
|
@ -158,7 +158,6 @@ class LicenseController extends BaseController
|
||||
|
||||
/* Catch claim license requests */
|
||||
if (config('ninja.environment') == 'selfhost') {
|
||||
// $response = Http::get( "http://ninja.test:8000/claim_license", [
|
||||
$response = Http::get("https://invoicing.co/claim_license", [
|
||||
'license_key' => $license_key,
|
||||
'product_id' => 3,
|
||||
|
@ -21,7 +21,7 @@ class ProtectedDownloadController extends BaseController
|
||||
public function index(Request $request, string $hash)
|
||||
{
|
||||
/** @var string $hashed_path */
|
||||
$hashed_path = Cache::pull($hash);
|
||||
$hashed_path = Cache::get($hash);
|
||||
|
||||
if (!$hashed_path) {
|
||||
throw new SystemError('File no longer available', 404);
|
||||
|
@ -30,18 +30,24 @@ class SmtpController extends BaseController
|
||||
$user = auth()->user();
|
||||
$company = $user->company();
|
||||
|
||||
|
||||
$smtp_host = $request->input('smtp_host', $company->smtp_host);
|
||||
$smtp_port = $request->input('smtp_port', $company->smtp_port);
|
||||
$smtp_username = $request->input('smtp_username', $company->smtp_username);
|
||||
$smtp_password = $request->input('smtp_password', $company->smtp_password);
|
||||
$smtp_encryption = $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls');
|
||||
$smtp_local_domain = $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null);
|
||||
$smtp_verify_peer = $request->input('verify_peer', $company->smtp_verify_peer ?? true);
|
||||
|
||||
config([
|
||||
'mail.mailers.smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'host' => $request->input('smtp_host', $company->smtp_host),
|
||||
'port' => $request->input('smtp_port', $company->smtp_port),
|
||||
'username' => $request->input('smtp_username', $company->smtp_username),
|
||||
'password' => $request->input('smtp_password', $company->smtp_password),
|
||||
'encryption' => $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls'),
|
||||
'local_domain' => $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null),
|
||||
'verify_peer' => $request->input('verify_peer', $company->smtp_verify_peer ?? true),
|
||||
'host' => $smtp_host,
|
||||
'port' => $smtp_port,
|
||||
'username' => $smtp_username,
|
||||
'password' => $smtp_password,
|
||||
'encryption' => $smtp_encryption,
|
||||
'local_domain' => $smtp_local_domain,
|
||||
'verify_peer' => $smtp_verify_peer,
|
||||
'timeout' => 5,
|
||||
],
|
||||
]);
|
||||
@ -49,7 +55,7 @@ class SmtpController extends BaseController
|
||||
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
|
||||
|
||||
try {
|
||||
Mail::to($user->email, $user->present()->name())->send(new TestMailServer('Email Server Works!', strlen($company->settings->custom_sending_email) > 1 ? $company->settings->custom_sending_email : $user->email));
|
||||
Mail::mailer('smtp')->to($user->email, $user->present()->name())->send(new TestMailServer('Email Server Works!', strlen($company->settings->custom_sending_email) > 1 ? $company->settings->custom_sending_email : $user->email));
|
||||
} catch (\Exception $e) {
|
||||
app('mail.manager')->forgetMailers();
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
|
@ -64,6 +64,8 @@ class StripeConnectController extends BaseController
|
||||
if ($request->has('error') && $request->error == 'access_denied') {
|
||||
return view('auth.connect.access_denied');
|
||||
}
|
||||
|
||||
$response = false;
|
||||
|
||||
try {
|
||||
/** @class \stdClass $response
|
||||
@ -88,6 +90,11 @@ class StripeConnectController extends BaseController
|
||||
nlog($response);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(!$response) {
|
||||
return view('auth.connect.access_denied');
|
||||
}
|
||||
|
||||
@ -144,11 +151,14 @@ class StripeConnectController extends BaseController
|
||||
if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) {
|
||||
$redirect_uri = config('ninja.react_url').'/#/settings/online_payments';
|
||||
} else {
|
||||
$redirect_uri = config('ninja.app_url').'/stripe/completed';
|
||||
$redirect_uri = config('ninja.app_url');
|
||||
}
|
||||
|
||||
\Illuminate\Support\Facades\Cache::pull($request->token);
|
||||
|
||||
//response here
|
||||
return view('auth.connect.completed', ['url' => $redirect_uri]);
|
||||
// return redirect($redirect_uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,6 +72,10 @@ class TwoFactorController extends BaseController
|
||||
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
|
||||
} elseif (! $secret || ! $google2fa->verifyKey($secret, $oneTimePassword)) {
|
||||
return response()->json(['message' => ctrans('texts.invalid_one_time_password')], 400);
|
||||
}elseif (! $user->phone) {
|
||||
return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400);
|
||||
} elseif (! $user->isVerified()) {
|
||||
return response()->json(['message' => 'Please confirm your account first'], 400);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'No phone record or user is not confirmed'], 400);
|
||||
|
@ -135,7 +135,7 @@ class Kernel extends HttpKernel
|
||||
'can' => Authorize::class,
|
||||
'cors' => Cors::class,
|
||||
'guest' => RedirectIfAuthenticated::class,
|
||||
'signed' => ValidateSignature::class,
|
||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'verified' => EnsureEmailIsVerified::class,
|
||||
'query_logging' => QueryLogging::class,
|
||||
'token_auth' => TokenAuth::class,
|
||||
|
@ -59,20 +59,6 @@ class StoreClientRequest extends Request
|
||||
$rules['file'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('clients')->where('company_id', $user->company()->id);
|
||||
}
|
||||
|
||||
$rules['country_id'] = 'integer|nullable';
|
||||
|
||||
if (isset($this->currency_code)) {
|
||||
$rules['currency_code'] = 'sometimes|exists:currencies,code';
|
||||
}
|
||||
|
||||
if (isset($this->country_code)) {
|
||||
$rules['country_code'] = new CountryCodeExistsRule();
|
||||
}
|
||||
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
//$rules['name'] = 'required|min:1';
|
||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||
@ -97,6 +83,9 @@ class StoreClientRequest extends Request
|
||||
$rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
||||
$rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
||||
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,company,partnership,trust,charity,government,other';
|
||||
$rules['shipping_country_id'] = 'integer|nullable|exists:countries,id';
|
||||
$rules['number'] = ['sometimes', 'nullable', 'bail', Rule::unique('clients')->where('company_id', $user->company()->id)];
|
||||
$rules['country_id'] = 'integer|nullable|exists:countries,id';
|
||||
|
||||
return $rules;
|
||||
}
|
||||
@ -139,12 +128,16 @@ class StoreClientRequest extends Request
|
||||
if (! array_key_exists('currency_id', $input['settings']) && isset($input['group_settings_id'])) {
|
||||
$group_settings = GroupSetting::find($input['group_settings_id']);
|
||||
|
||||
if ($group_settings && property_exists($group_settings->settings, 'currency_id') && isset($group_settings->settings->currency_id)) {
|
||||
if ($group_settings && property_exists($group_settings->settings, 'currency_id') && is_numeric($group_settings->settings->currency_id)) {
|
||||
$input['settings']['currency_id'] = (string) $group_settings->settings->currency_id;
|
||||
} else {
|
||||
$input['settings']['currency_id'] = (string) $user->company()->settings->currency_id;
|
||||
}
|
||||
} elseif (! array_key_exists('currency_id', $input['settings'])) {
|
||||
}
|
||||
elseif (! array_key_exists('currency_id', $input['settings'])) {
|
||||
$input['settings']['currency_id'] = (string) $user->company()->settings->currency_id;
|
||||
}
|
||||
elseif (empty($input['settings']['currency_id']) ?? true) {
|
||||
$input['settings']['currency_id'] = (string) $user->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
@ -160,10 +153,13 @@ class StoreClientRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// allow setting country_id by iso code
|
||||
if (isset($input['country_code'])) {
|
||||
$input['country_id'] = $this->getCountryCode($input['country_code']);
|
||||
}
|
||||
|
||||
// allow setting country_id by iso code
|
||||
if (isset($input['shipping_country_code'])) {
|
||||
$input['shipping_country_id'] = $this->getCountryCode($input['shipping_country_code']);
|
||||
}
|
||||
@ -173,10 +169,14 @@ class StoreClientRequest extends Request
|
||||
unset($input['number']);
|
||||
}
|
||||
|
||||
// prevent xss injection
|
||||
if (array_key_exists('name', $input)) {
|
||||
$input['name'] = strip_tags($input['name']);
|
||||
}
|
||||
|
||||
//If you want to validate, the prop must be set.
|
||||
$input['id'] = null;
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -60,17 +60,11 @@ class UpdateClientRequest extends Request
|
||||
$rules['company_logo'] = 'mimes:jpeg,jpg,png,gif|max:10000';
|
||||
$rules['industry_id'] = 'integer|nullable';
|
||||
$rules['size_id'] = 'integer|nullable';
|
||||
$rules['country_id'] = 'integer|nullable';
|
||||
$rules['shipping_country_id'] = 'integer|nullable';
|
||||
$rules['country_id'] = 'integer|nullable|exists:countries,id';
|
||||
$rules['shipping_country_id'] = 'integer|nullable|exists:countries,id';
|
||||
$rules['classification'] = 'bail|sometimes|nullable|in:individual,business,company,partnership,trust,charity,government,other';
|
||||
|
||||
if ($this->id_number) {
|
||||
$rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id);
|
||||
}
|
||||
|
||||
if ($this->number) {
|
||||
$rules['number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id);
|
||||
}
|
||||
$rules['id_number'] = ['sometimes', 'bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id)];
|
||||
$rules['number'] = ['sometimes', 'bail', Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id)];
|
||||
|
||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||
$rules['contacts'] = 'array';
|
||||
@ -112,6 +106,9 @@ class UpdateClientRequest extends Request
|
||||
if (array_key_exists('settings', $input) && ! array_key_exists('currency_id', $input['settings'])) {
|
||||
$input['settings']['currency_id'] = (string) $user->company()->settings->currency_id;
|
||||
}
|
||||
elseif (empty($input['settings']['currency_id']) ?? true) {
|
||||
$input['settings']['currency_id'] = (string) $user->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
if (isset($input['language_code'])) {
|
||||
$input['settings']['language_id'] = $this->getLanguageId($input['language_code']);
|
||||
@ -127,9 +124,35 @@ class UpdateClientRequest extends Request
|
||||
$input['name'] = strip_tags($input['name']);
|
||||
}
|
||||
|
||||
// allow setting country_id by iso code
|
||||
if (isset($input['country_code'])) {
|
||||
$input['country_id'] = $this->getCountryCode($input['country_code']);
|
||||
}
|
||||
|
||||
// allow setting country_id by iso code
|
||||
if (isset($input['shipping_country_code'])) {
|
||||
$input['shipping_country_id'] = $this->getCountryCode($input['shipping_country_code']);
|
||||
}
|
||||
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
private function getCountryCode($country_code)
|
||||
{
|
||||
$countries = Cache::get('countries');
|
||||
|
||||
$country = $countries->filter(function ($item) use ($country_code) {
|
||||
return $item->iso_3166_2 == $country_code || $item->iso_3166_3 == $country_code;
|
||||
})->first();
|
||||
|
||||
if ($country) {
|
||||
return (string) $country->id;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getLanguageId($language_code)
|
||||
{
|
||||
$languages = Cache::get('languages');
|
||||
|
@ -57,7 +57,7 @@ class StoreCompanyRequest extends Request
|
||||
}
|
||||
|
||||
$rules['smtp_host'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_port'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_port'] = 'sometimes|integer|nullable';
|
||||
$rules['smtp_encryption'] = 'sometimes|string';
|
||||
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_encryption'] = 'sometimes|string|nullable';
|
||||
@ -96,6 +96,10 @@ class StoreCompanyRequest extends Request
|
||||
unset($input['smtp_password']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_port'])) {
|
||||
$input['smtp_port'] = (int) $input['smtp_port'];
|
||||
}
|
||||
|
||||
if(isset($input['smtp_verify_peer']) && is_string($input['smtp_verify_peer']))
|
||||
$input['smtp_verify_peer'] == 'true' ? true : false;
|
||||
|
||||
|
@ -59,7 +59,7 @@ class UpdateCompanyRequest extends Request
|
||||
$rules['e_invoice_certificate'] = 'sometimes|nullable|file|mimes:p12,pfx,pem,cer,crt,der,txt,p7b,spc,bin';
|
||||
|
||||
$rules['smtp_host'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_port'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_port'] = 'sometimes|integer|nullable';
|
||||
$rules['smtp_encryption'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
||||
// $rules['smtp_verify_peer'] = 'sometimes|string';
|
||||
@ -105,6 +105,10 @@ class UpdateCompanyRequest extends Request
|
||||
unset($input['smtp_password']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_port'])) {
|
||||
$input['smtp_port'] = (int)$input['smtp_port'];
|
||||
}
|
||||
|
||||
if(isset($input['smtp_verify_peer']) && is_string($input['smtp_verify_peer'])) {
|
||||
$input['smtp_verify_peer'] == 'true' ? true : false;
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ class StoreInvoiceRequest extends Request
|
||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date'];
|
||||
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
|
||||
|
||||
|
||||
return $rules;
|
||||
@ -112,6 +113,12 @@ class StoreInvoiceRequest extends Request
|
||||
$input['exchange_rate'] = 1;
|
||||
}
|
||||
|
||||
//handles edge case where we need for force set the due date of the invoice.
|
||||
if((isset($input['partial_due_date']) && strlen($input['partial_due_date']) > 1) && (!array_key_exists('due_date', $input) || (empty($input['due_date']) && empty($this->invoice->due_date)))) {
|
||||
$client = \App\Models\Client::withTrashed()->find($input['client_id']);
|
||||
$input['due_date'] = \Illuminate\Support\Carbon::parse($input['date'])->addDays($client->getSetting('payment_terms'))->format('Y-m-d');
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ class UpdateInvoiceRequest extends Request
|
||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
$rules['partial'] = 'bail|sometimes|nullable|numeric';
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
|
||||
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
|
||||
|
||||
|
||||
return $rules;
|
||||
@ -107,6 +108,12 @@ class UpdateInvoiceRequest extends Request
|
||||
$input['exchange_rate'] = 1;
|
||||
}
|
||||
|
||||
//handles edge case where we need for force set the due date of the invoice.
|
||||
if((isset($input['partial_due_date']) && strlen($input['partial_due_date']) > 1) && (!array_key_exists('due_date', $input) || (empty($input['due_date']) && empty($this->invoice->due_date)))) {
|
||||
$client = \App\Models\Client::withTrashed()->find($input['client_id']);
|
||||
$input['due_date'] = \Illuminate\Support\Carbon::parse($input['date'])->addDays($client->getSetting('payment_terms'))->format('Y-m-d');
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ class StoreProjectRequest extends Request
|
||||
|
||||
$rules['name'] = 'required';
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
|
||||
$rules['budgeted_hours'] = 'sometimes|numeric';
|
||||
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id);
|
||||
@ -74,6 +75,9 @@ class StoreProjectRequest extends Request
|
||||
$input['color'] = '';
|
||||
}
|
||||
|
||||
if(array_key_exists('budgeted_hours', $input) && empty($input['budgeted_hours']))
|
||||
$input['budgeted_hours'] = 0;
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ class UpdateProjectRequest extends Request
|
||||
$rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id);
|
||||
}
|
||||
|
||||
$rules['budgeted_hours'] = 'sometimes|numeric';
|
||||
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
@ -73,6 +75,10 @@ class UpdateProjectRequest extends Request
|
||||
if (array_key_exists('color', $input) && is_null($input['color'])) {
|
||||
$input['color'] = '';
|
||||
}
|
||||
|
||||
if(array_key_exists('budgeted_hours', $input) && empty($input['budgeted_hours'])) {
|
||||
$input['budgeted_hours'] = 0;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -37,7 +37,8 @@ class GenericReportRequest extends Request
|
||||
'start_date' => 'bail|required_if:date_range,custom|nullable|date',
|
||||
'report_keys' => 'present|array',
|
||||
'send_email' => 'required|bool',
|
||||
'document_email_attachment' => 'sometimes|bool'
|
||||
'document_email_attachment' => 'sometimes|bool',
|
||||
'include_deleted' => 'required|bool',
|
||||
// 'status' => 'sometimes|string|nullable|in:all,draft,sent,viewed,paid,unpaid,overdue',
|
||||
];
|
||||
}
|
||||
@ -63,6 +64,8 @@ class GenericReportRequest extends Request
|
||||
$input['end_date'] = null;
|
||||
}
|
||||
|
||||
$input['include_deleted'] = array_key_exists('include_deleted', $input) ? filter_var($input['include_deleted'], FILTER_VALIDATE_BOOLEAN) : false;
|
||||
|
||||
$input['user_id'] = auth()->user()->id;
|
||||
|
||||
$this->replace($input);
|
||||
|
@ -36,18 +36,46 @@ class CheckSmtpRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'smtp_host' => 'sometimes|nullable|string|min:3',
|
||||
'smtp_port' => 'sometimes|nullable|integer',
|
||||
'smtp_username' => 'sometimes|nullable|string|min:3',
|
||||
'smtp_password' => 'sometimes|nullable|string|min:3',
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
$company = $user->company();
|
||||
|
||||
$input = $this->input();
|
||||
|
||||
if(isset($input['smtp_username']) && $input['smtp_username'] == '********')
|
||||
unset($input['smtp_username']);
|
||||
if(isset($input['smtp_username']) && $input['smtp_username'] == '********'){
|
||||
// unset($input['smtp_username']);
|
||||
$input['smtp_username'] = $company->smtp_username;
|
||||
}
|
||||
|
||||
if(isset($input['smtp_password'])&& $input['smtp_password'] == '********'){
|
||||
// unset($input['smtp_password']);
|
||||
$input['smtp_password'] = $company->smtp_password;
|
||||
}
|
||||
|
||||
if(isset($input['smtp_host']) && strlen($input['smtp_host']) >=3){
|
||||
|
||||
}
|
||||
else {
|
||||
$input['smtp_host'] = $company->smtp_host;
|
||||
}
|
||||
|
||||
|
||||
if(isset($input['smtp_port']) && strlen($input['smtp_port']) >= 3) {
|
||||
|
||||
} else {
|
||||
$input['smtp_port'] = $company->smtp_port;
|
||||
}
|
||||
|
||||
if(isset($input['smtp_password'])&& $input['smtp_password'] == '********')
|
||||
unset($input['smtp_password']);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -13,11 +13,30 @@ namespace App\Http\Requests\TaskScheduler;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Scheduler\ValidClientIds;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class StoreSchedulerRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
public array $client_statuses = [
|
||||
'all',
|
||||
'draft',
|
||||
'paid',
|
||||
'unpaid',
|
||||
'overdue',
|
||||
'pending',
|
||||
'invoiced',
|
||||
'logged',
|
||||
'partial',
|
||||
'applied',
|
||||
'active',
|
||||
'paused',
|
||||
'completed',
|
||||
'approved',
|
||||
'expired',
|
||||
'upcoming',
|
||||
'converted',
|
||||
'uninvoiced',
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@ -47,7 +66,7 @@ class StoreSchedulerRequest extends Request
|
||||
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
|
||||
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
|
||||
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
|
||||
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,client,contact,client_contact,credit,document,expense,invoice,invoice_item,quote,quote_item,recurring_invoice,payment,product,task'],
|
||||
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,activities,client,clients,client_contact,client_contacts,credit,credits,document,documents,expense,expenses,invoice,invoices,invoice_item,invoice_items,quote,quotes,quote_item,quote_items,recurring_invoice,recurring_invoices,payment,payments,product,products,task,tasks'],
|
||||
'parameters.date_key' => ['bail','sometimes', 'string'],
|
||||
'parameters.status' => ['bail','sometimes', 'nullable', 'string'],
|
||||
];
|
||||
@ -73,10 +92,18 @@ class StoreSchedulerRequest extends Request
|
||||
|
||||
if(isset($input['parameters']['status'])) {
|
||||
|
||||
$task_statuses = [];
|
||||
|
||||
if(isset($input['parameters']['report_name']) && $input['parameters']['report_name'] == 'task') {
|
||||
$task_statuses = array_diff(explode(",", $input['parameters']['status']), $this->client_statuses);
|
||||
}
|
||||
|
||||
$input['parameters']['status'] = collect(explode(",", $input['parameters']['status']))
|
||||
->filter(function ($status) {
|
||||
return in_array($status, ['all','draft','paid','unpaid','overdue']);
|
||||
})->implode(",") ?? '';
|
||||
return in_array($status, $this->client_statuses);
|
||||
})->merge($task_statuses)
|
||||
->implode(",") ?? '';
|
||||
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
|
@ -16,6 +16,27 @@ use App\Http\ValidationRules\Scheduler\ValidClientIds;
|
||||
|
||||
class UpdateSchedulerRequest extends Request
|
||||
{
|
||||
public array $client_statuses = [
|
||||
'all',
|
||||
'draft',
|
||||
'paid',
|
||||
'unpaid',
|
||||
'overdue',
|
||||
'pending',
|
||||
'invoiced',
|
||||
'logged',
|
||||
'partial',
|
||||
'applied',
|
||||
'active',
|
||||
'paused',
|
||||
'completed',
|
||||
'approved',
|
||||
'expired',
|
||||
'upcoming',
|
||||
'converted',
|
||||
'uninvoiced',
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@ -45,9 +66,9 @@ class UpdateSchedulerRequest extends Request
|
||||
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
|
||||
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
|
||||
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
|
||||
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,client,contact,client_contact,credit,document,expense,invoice,invoice_item,quote,quote_item,recurring_invoice,payment,product,task'],
|
||||
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,activities,client,clients,client_contact,client_contacts,credit,credits,document,documents,expense,expenses,invoice,invoices,invoice_item,invoice_items,quote,quotes,quote_item,quote_items,recurring_invoice,recurring_invoices,payment,payments,product,products,task,tasks'],
|
||||
'parameters.date_key' => ['bail','sometimes', 'string'],
|
||||
'parameters.status' => ['bail','sometimes', 'string'],
|
||||
'parameters.status' => ['bail','sometimes', 'nullable', 'string'],
|
||||
];
|
||||
|
||||
return $rules;
|
||||
@ -71,10 +92,18 @@ class UpdateSchedulerRequest extends Request
|
||||
|
||||
if(isset($input['parameters']['status'])) {
|
||||
|
||||
|
||||
$task_statuses = [];
|
||||
|
||||
if(isset($input['parameters']['report_name']) && $input['parameters']['report_name'] == 'task') {
|
||||
$task_statuses = array_diff(explode(",", $input['parameters']['status']), $this->client_statuses);
|
||||
}
|
||||
|
||||
$input['parameters']['status'] = collect(explode(",", $input['parameters']['status']))
|
||||
->filter(function ($status) {
|
||||
return in_array($status, ['all','draft','paid','unpaid','overdue']);
|
||||
})->implode(",") ?? '';
|
||||
return in_array($status, $this->client_statuses);
|
||||
})->merge($task_statuses)
|
||||
->implode(",") ?? '';
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
|
@ -95,6 +95,8 @@ class StoreUserRequest extends Request
|
||||
$input['last_name'] = strip_tags($input['last_name']);
|
||||
}
|
||||
|
||||
$input['id'] = null;
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,10 @@ class StoreWebhookRequest extends Request
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin() && auth()->user()->account->hasFeature(Account::FEATURE_API);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->isAdmin() && $user->account->hasFeature(Account::FEATURE_API);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
@ -31,7 +34,6 @@ class StoreWebhookRequest extends Request
|
||||
return [
|
||||
'target_url' => 'bail|required|url',
|
||||
'event_id' => 'bail|required',
|
||||
// 'headers' => 'bail|sometimes|json',
|
||||
'rest_method' => 'required|in:post,put'
|
||||
];
|
||||
}
|
||||
@ -43,9 +45,7 @@ class StoreWebhookRequest extends Request
|
||||
if (!isset($input['rest_method'])) {
|
||||
$input['rest_method'] = 'post';
|
||||
}
|
||||
// if(isset($input['headers']) && count($input['headers']) == 0)
|
||||
// $input['headers'] = null;
|
||||
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +153,7 @@ class BaseImport
|
||||
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line **/
|
||||
return $bestDelimiter ?? ',';
|
||||
}
|
||||
|
||||
|
@ -98,19 +98,19 @@ class InvoiceTransformer extends BaseTransformer
|
||||
$invoice_data,
|
||||
'invoice.partial_due_date'
|
||||
),
|
||||
'custom_surcharge1' => $this->getString(
|
||||
'custom_surcharge1' => $this->getFloat(
|
||||
$invoice_data,
|
||||
'invoice.custom_surcharge1'
|
||||
),
|
||||
'custom_surcharge2' => $this->getString(
|
||||
'custom_surcharge2' => $this->getFloat(
|
||||
$invoice_data,
|
||||
'invoice.custom_surcharge2'
|
||||
),
|
||||
'custom_surcharge3' => $this->getString(
|
||||
'custom_surcharge3' => $this->getFloat(
|
||||
$invoice_data,
|
||||
'invoice.custom_surcharge3'
|
||||
),
|
||||
'custom_surcharge4' => $this->getString(
|
||||
'custom_surcharge4' => $this->getFloat(
|
||||
$invoice_data,
|
||||
'invoice.custom_surcharge4'
|
||||
),
|
||||
|
@ -114,23 +114,26 @@ class ProcessBankTransactionsNordigen implements ShouldQueue
|
||||
|
||||
private function updateAccount()
|
||||
{
|
||||
if (!$this->nordigen->isAccountActive($this->bank_integration->nordigen_account_id)) {
|
||||
$is_account_active = $this->nordigen->isAccountActive($this->bank_integration->nordigen_account_id);
|
||||
$account = $this->nordigen->getAccount($this->bank_integration->nordigen_account_id);
|
||||
|
||||
if (!$is_account_active || !$account) {
|
||||
$this->bank_integration->disabled_upstream = true;
|
||||
$this->bank_integration->save();
|
||||
$this->stop_loop = false;
|
||||
|
||||
nlog("Nordigen: account inactive: " . $this->bank_integration->nordigen_account_id);
|
||||
// @turbo124 @todo send email for expired account
|
||||
|
||||
$this->nordigen->disabledAccountEmail($this->bank_integration);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->nordigen_account = $this->nordigen->getAccount($this->bank_integration->nordigen_account_id);
|
||||
$this->nordigen_account = $account;
|
||||
|
||||
$this->bank_integration->disabled_upstream = false;
|
||||
$this->bank_integration->bank_account_status = $this->nordigen_account['account_status'];
|
||||
$this->bank_integration->balance = $this->nordigen_account['current_balance'];
|
||||
$this->bank_integration->bank_account_status = $account['account_status'];
|
||||
$this->bank_integration->balance = $account['current_balance'];
|
||||
|
||||
$this->bank_integration->save();
|
||||
}
|
||||
@ -138,7 +141,7 @@ class ProcessBankTransactionsNordigen implements ShouldQueue
|
||||
private function processTransactions()
|
||||
{
|
||||
//Get transaction count object
|
||||
$transactions = $this->nordigen->getTransactions($this->bank_integration->nordigen_account_id, $this->from_date);
|
||||
$transactions = $this->nordigen->getTransactions($this->company, $this->bank_integration->nordigen_account_id, $this->from_date);
|
||||
|
||||
//if no transactions, update the from_date and move on
|
||||
if (count($transactions) == 0) {
|
||||
|
@ -69,10 +69,9 @@ class CompanyExport implements ShouldQueue
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
|
||||
$this->file_name = date('Y-m-d') . '_' . str_replace([" ", "/"], ["_",""], $this->company->present()->name() . '_' . $this->company->company_key . '.json');
|
||||
|
||||
$this->writer = new File($this->file_name);
|
||||
$this->writer = new File(sys_get_temp_dir().'/'.$this->file_name);
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
@ -114,8 +113,6 @@ class CompanyExport implements ShouldQueue
|
||||
return $user;
|
||||
})->all();
|
||||
|
||||
|
||||
|
||||
$x = $this->writer->collection('users');
|
||||
$x->addItems($this->export_data['users']);
|
||||
$this->export_data = null;
|
||||
@ -667,7 +664,7 @@ class CompanyExport implements ShouldQueue
|
||||
private function zipAndSend()
|
||||
{
|
||||
|
||||
$zip_path = \Illuminate\Support\Str::ascii(str_replace(".json", ".zip", $this->file_name));
|
||||
$zip_path = sys_get_temp_dir().'/'.\Illuminate\Support\Str::ascii(str_replace(".json", ".zip", $this->file_name));
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
@ -675,8 +672,8 @@ class CompanyExport implements ShouldQueue
|
||||
nlog("cannot open {$zip_path}");
|
||||
}
|
||||
|
||||
$zip->addFile($this->file_name);
|
||||
$zip->renameName($this->file_name, 'backup.json');
|
||||
$zip->addFile(sys_get_temp_dir().'/'.$this->file_name, 'backup.json');
|
||||
// $zip->renameName($this->file_name, 'backup.json');
|
||||
|
||||
$zip->close();
|
||||
|
||||
@ -686,8 +683,8 @@ class CompanyExport implements ShouldQueue
|
||||
unlink($zip_path);
|
||||
}
|
||||
|
||||
if(file_exists($this->file_name)) {
|
||||
unlink($this->file_name);
|
||||
if(file_exists(sys_get_temp_dir().'/'.$this->file_name)) {
|
||||
unlink(sys_get_temp_dir().'/'.$this->file_name);
|
||||
}
|
||||
|
||||
if(Ninja::isSelfHost()) {
|
||||
@ -717,8 +714,8 @@ class CompanyExport implements ShouldQueue
|
||||
if (Ninja::isHosted()) {
|
||||
sleep(3);
|
||||
|
||||
if(file_exists($zip_path)) {
|
||||
unlink($zip_path);
|
||||
if(file_exists(sys_get_temp_dir().'/'.$zip_path)) {
|
||||
unlink(sys_get_temp_dir().'/'.$zip_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,8 +311,11 @@ class CompanyImport implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
unlink($tmp_file);
|
||||
unlink(Storage::path($this->file_location));
|
||||
if(file_exists($tmp_file))
|
||||
unlink($tmp_file);
|
||||
|
||||
if(Storage::exists($this->file_location))
|
||||
unlink(Storage::path($this->file_location));
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -150,7 +150,8 @@ class NinjaMailerJob implements ShouldQueue
|
||||
->send($mailable);
|
||||
|
||||
/* Count the amount of emails sent across all the users accounts */
|
||||
Cache::increment("email_quota" . $this->company->account->key);
|
||||
|
||||
$this->incrementEmailCounter();
|
||||
|
||||
LightLogs::create(new EmailSuccess($this->nmo->company->company_key, $this->nmo->mailable->subject))
|
||||
->send();
|
||||
@ -226,6 +227,12 @@ class NinjaMailerJob implements ShouldQueue
|
||||
$this->cleanUpMailers();
|
||||
}
|
||||
|
||||
private function incrementEmailCounter(): void
|
||||
{
|
||||
if(in_array($this->mailer, ['default','mailgun']))
|
||||
Cache::increment("email_quota".$this->company->account->key);
|
||||
|
||||
}
|
||||
/**
|
||||
* Entity notification when an email fails to send
|
||||
*
|
||||
@ -284,13 +291,21 @@ class NinjaMailerJob implements ShouldQueue
|
||||
$this->mailer = 'postmark';
|
||||
$this->client_postmark_secret = config('services.postmark-outlook.token');
|
||||
|
||||
if (property_exists($this->nmo->settings, 'email_from_name') && strlen($this->nmo->settings->email_from_name) > 1) {
|
||||
$email_from_name = $this->nmo->settings->email_from_name;
|
||||
} else {
|
||||
$email_from_name = $this->company->present()->name();
|
||||
}
|
||||
|
||||
$this->nmo
|
||||
->mailable
|
||||
->from('maildelivery@invoice.services', 'Invoice Ninja');
|
||||
->from(config('services.postmark-outlook.from.address'), $email_from_name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
|
||||
nlog("problem switching outlook driver - hosted");
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
}
|
||||
@ -321,11 +336,10 @@ class NinjaMailerJob implements ShouldQueue
|
||||
$this->mailer = 'mailgun';
|
||||
$this->setMailgunMailer();
|
||||
return $this;
|
||||
case 'client_brevo':
|
||||
$this->mailer = 'brevo';
|
||||
$this->setBrevoMailer();
|
||||
case 'smtp':
|
||||
$this->mailer = 'smtp';
|
||||
$this->configureSmtpMailer();
|
||||
return $this;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -337,6 +351,48 @@ class NinjaMailerJob implements ShouldQueue
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function configureSmtpMailer(): void
|
||||
{
|
||||
|
||||
$company = $this->company;
|
||||
|
||||
$smtp_host = $company->smtp_host;
|
||||
$smtp_port = $company->smtp_port;
|
||||
$smtp_username = $company->smtp_username;
|
||||
$smtp_password = $company->smtp_password;
|
||||
$smtp_encryption = $company->smtp_encryption ?? 'tls';
|
||||
$smtp_local_domain = strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null;
|
||||
$smtp_verify_peer = $company->smtp_verify_peer ?? true;
|
||||
|
||||
config([
|
||||
'mail.mailers.smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'host' => $smtp_host,
|
||||
'port' => $smtp_port,
|
||||
'username' => $smtp_username,
|
||||
'password' => $smtp_password,
|
||||
'encryption' => $smtp_encryption,
|
||||
'local_domain' => $smtp_local_domain,
|
||||
'verify_peer' => $smtp_verify_peer,
|
||||
'timeout' => 30,
|
||||
],
|
||||
]);
|
||||
|
||||
if (property_exists($this->nmo->settings, 'email_from_name') && strlen($this->nmo->settings->email_from_name) > 1) {
|
||||
$email_from_name = $this->nmo->settings->email_from_name;
|
||||
} else {
|
||||
$email_from_name = $this->company->present()->name();
|
||||
}
|
||||
|
||||
$user = $this->resolveSendingUser();
|
||||
$sending_email = (isset($this->nmo->settings->custom_sending_email) && stripos($this->nmo->settings->custom_sending_email, "@")) ? $this->nmo->settings->custom_sending_email : $user->email;
|
||||
|
||||
$this->nmo
|
||||
->mailable
|
||||
->from($sending_email, $email_from_name);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration of multiple mailers
|
||||
* per company for use by self hosted users
|
||||
|
@ -60,6 +60,7 @@ class TaskScheduler implements ShouldQueue
|
||||
nlog("Doing job {$scheduler->name}");
|
||||
|
||||
try {
|
||||
//@var \App\Models\Schedule $scheduler
|
||||
$scheduler->service()->runTask();
|
||||
} catch(\Exception $e) {
|
||||
nlog($e->getMessage());
|
||||
|
@ -420,7 +420,7 @@ class Import implements ShouldQueue
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
|
||||
$data['subdomain'] = str_replace("_", "", $data['subdomain']);
|
||||
$data['subdomain'] = str_replace("_", "", ($data['subdomain'] ?? ''));
|
||||
|
||||
if (!MultiDB::checkDomainAvailable($data['subdomain'])) {
|
||||
$data['subdomain'] = MultiDB::randomSubdomainGenerator();
|
||||
|
@ -26,7 +26,6 @@ use App\Utils\Traits\MakesReminders;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Spatie\OpenTelemetry\Jobs\TraceAware;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
|
@ -123,7 +123,7 @@ class WebhookSingle implements ShouldQueue
|
||||
]);
|
||||
|
||||
(new SystemLogger(
|
||||
array_merge((array) $response, $data),
|
||||
['message' => $response->getHeaders(), 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_SUCCESS,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -136,7 +136,7 @@ class WebhookSingle implements ShouldQueue
|
||||
nlog($e->getMessage());
|
||||
|
||||
(new SystemLogger(
|
||||
['message' => "Error connecting to ". $subscription->target_url],
|
||||
['message' => "Error connecting to ". $subscription->target_url, 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -152,7 +152,7 @@ class WebhookSingle implements ShouldQueue
|
||||
$message = "There was a problem when connecting to {$subscription->target_url} => status code ". $e->getResponse()->getStatusCode(). " This webhook call will be suspended until further action is taken.";
|
||||
|
||||
(new SystemLogger(
|
||||
['message' => $message],
|
||||
['message' => $message, 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -170,7 +170,7 @@ class WebhookSingle implements ShouldQueue
|
||||
nlog($message);
|
||||
|
||||
(new SystemLogger(
|
||||
['message' => $message],
|
||||
['message' => $message, 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -192,7 +192,7 @@ class WebhookSingle implements ShouldQueue
|
||||
$message = "There was a problem when connecting to {$subscription->target_url} => status code ". $e->getResponse()->getStatusCode(). " no retry attempted.";
|
||||
|
||||
(new SystemLogger(
|
||||
['message' => $message],
|
||||
['message' => $message, 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -208,7 +208,7 @@ class WebhookSingle implements ShouldQueue
|
||||
$error = json_decode($e->getResponse()->getBody()->getContents());
|
||||
|
||||
(new SystemLogger(
|
||||
['message' => $error],
|
||||
['message' => $error, 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -220,7 +220,7 @@ class WebhookSingle implements ShouldQueue
|
||||
$error = json_decode($e->getResponse()->getBody()->getContents());
|
||||
|
||||
(new SystemLogger(
|
||||
['message' => $error],
|
||||
['message' => $error, 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
@ -232,7 +232,7 @@ class WebhookSingle implements ShouldQueue
|
||||
nlog($e->getCode());
|
||||
|
||||
(new SystemLogger(
|
||||
$e->getMessage(),
|
||||
['message' => $e->getMessage(), 'body' => $data],
|
||||
SystemLog::CATEGORY_WEBHOOK,
|
||||
SystemLog::EVENT_WEBHOOK_FAILURE,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
|
@ -149,6 +149,7 @@ class PdfSlot extends Component
|
||||
return render('components.livewire.pdf-slot', [
|
||||
'invitation' => $this->invitation,
|
||||
'entity' => $this->entity,
|
||||
'settings' => $this->settings,
|
||||
'data' => $this->invitation->company->settings,
|
||||
'entity_type' => $this->entity_type,
|
||||
'products' => $this->getProducts(),
|
||||
|
@ -12,15 +12,17 @@
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use Livewire\Component;
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Invoice;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
|
||||
class RequiredClientInfo extends Component
|
||||
{
|
||||
@ -31,10 +33,7 @@ class RequiredClientInfo extends Component
|
||||
*/
|
||||
public $show_terms = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $invoice;
|
||||
public $invoice_terms;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
@ -49,18 +48,40 @@ class RequiredClientInfo extends Component
|
||||
/**
|
||||
* @var ClientContact
|
||||
*/
|
||||
public $contact;
|
||||
public $contact_id;
|
||||
|
||||
/**
|
||||
* @var \App\Models\Client
|
||||
*/
|
||||
public $client;
|
||||
public $client_id;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $countries;
|
||||
|
||||
|
||||
public $client_name;
|
||||
public $contact_first_name;
|
||||
public $contact_last_name;
|
||||
public $contact_email;
|
||||
public $client_phone;
|
||||
public $client_address_line_1;
|
||||
public $client_city;
|
||||
public $client_state;
|
||||
public $client_country_id;
|
||||
public $client_postal_code;
|
||||
public $client_shipping_address_line_1;
|
||||
public $client_shipping_city;
|
||||
public $client_shipping_state;
|
||||
public $client_shipping_postal_code;
|
||||
public $client_shipping_country_id;
|
||||
public $client_custom_value1;
|
||||
public $client_custom_value2;
|
||||
public $client_custom_value3;
|
||||
public $client_custom_value4;
|
||||
|
||||
|
||||
/**
|
||||
* Mappings for updating the database. Left side is mapping from gateway,
|
||||
* right side is column in database.
|
||||
@ -113,50 +134,96 @@ class RequiredClientInfo extends Component
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'client.address1' => '',
|
||||
'client.address2' => '',
|
||||
'client.city' => '',
|
||||
'client.state' => '',
|
||||
'client.postal_code' => '',
|
||||
'client.country_id' => '',
|
||||
'client.shipping_address1' => '',
|
||||
'client.shipping_address2' => '',
|
||||
'client.shipping_city' => '',
|
||||
'client.shipping_state' => '',
|
||||
'client.shipping_postal_code' => '',
|
||||
'client.shipping_country_id' => '',
|
||||
'contact.first_name' => '',
|
||||
'contact.last_name' => '',
|
||||
'contact.email' => '',
|
||||
'client.name' => '',
|
||||
'client.website' => '',
|
||||
'client.phone' => '',
|
||||
'client.custom_value1' => '',
|
||||
'client.custom_value2' => '',
|
||||
'client.custom_value3' => '',
|
||||
'client.custom_value4' => '',
|
||||
// 'client.address1' => '',
|
||||
// 'client.address2' => '',
|
||||
// 'client.city' => '',
|
||||
// 'client.state' => '',
|
||||
// 'client.postal_code' => '',
|
||||
// 'client.country_id' => '',
|
||||
// 'client.shipping_address1' => '',
|
||||
// 'client.shipping_address2' => '',
|
||||
// 'client.shipping_city' => '',
|
||||
// 'client.shipping_state' => '',
|
||||
// 'client.shipping_postal_code' => '',
|
||||
// 'client.shipping_country_id' => '',
|
||||
// 'contact.first_name' => '',
|
||||
// 'contact.last_name' => '',
|
||||
// 'contact.email' => '',
|
||||
// 'client.name' => '',
|
||||
// 'client.website' => '',
|
||||
// 'client.phone' => '',
|
||||
// 'client.custom_value1' => '',
|
||||
// 'client.custom_value2' => '',
|
||||
// 'client.custom_value3' => '',
|
||||
// 'client.custom_value4' => '',
|
||||
'client_name' => '',
|
||||
'client_website' => '',
|
||||
'client_phone' => '',
|
||||
'client_address_line_1' => '',
|
||||
'client_address_line_2' => '',
|
||||
'client_city' => '',
|
||||
'client_state' => '',
|
||||
'client_postal_code' => '',
|
||||
'client_country_id' => '',
|
||||
'client_shipping_address_line_1' => '',
|
||||
'client_shipping_address_line_2' => '',
|
||||
'client_shipping_city' => '',
|
||||
'client_shipping_state' => '',
|
||||
'client_shipping_postal_code' => '',
|
||||
'client_shipping_country_id' => '',
|
||||
'client_custom_value1' => '',
|
||||
'client_custom_value2' => '',
|
||||
'client_custom_value3' => '',
|
||||
'client_custom_value4' => '',
|
||||
'contact_first_name' => '',
|
||||
'contact_last_name' => '',
|
||||
'contact_email' => '',
|
||||
];
|
||||
|
||||
public $show_form = false;
|
||||
|
||||
public $company;
|
||||
public $company_id;
|
||||
|
||||
public $company_gateway_id;
|
||||
|
||||
public $db;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
$contact = ClientContact::withTrashed()->find($this->contact_id);
|
||||
$company = $contact->company;
|
||||
|
||||
$this->client = $this->contact->client;
|
||||
$this->client_name = $contact->client->name;
|
||||
$this->contact_first_name = $contact->first_name;
|
||||
$this->contact_last_name = $contact->last_name;
|
||||
$this->contact_email = $contact->email;
|
||||
$this->client_phone = $contact->client->phone;
|
||||
$this->client_address_line_1 = $contact->client->address1;
|
||||
$this->client_city = $contact->client->city ;
|
||||
$this->client_state = $contact->client->state;
|
||||
$this->client_country_id = $contact->client->country_id;
|
||||
$this->client_postal_code = $contact->client->postal_code;
|
||||
$this->client_shipping_address_line_1 = $contact->client->shipping_address1;
|
||||
$this->client_shipping_city = $contact->client->shipping_city;
|
||||
$this->client_shipping_state = $contact->client->shipping_state;
|
||||
$this->client_shipping_postal_code = $contact->client->shipping_postal_code;
|
||||
$this->client_shipping_country_id = $contact->client->shipping_country_id;
|
||||
$this->client_custom_value1 = $contact->client->custom_value1;
|
||||
$this->client_custom_value2 = $contact->client->custom_value2;
|
||||
$this->client_custom_value3 = $contact->client->custom_value3;
|
||||
$this->client_custom_value4 = $contact->client->custom_value4;
|
||||
|
||||
if ($this->company->settings->show_accept_invoice_terms && request()->query('hash')) {
|
||||
// $this->client = $this->contact->client;
|
||||
|
||||
if ($company->settings->show_accept_invoice_terms && request()->query('hash')) {
|
||||
$this->show_terms = true;
|
||||
$this->terms_accepted = false;
|
||||
$this->show_form = true;
|
||||
|
||||
$hash = Cache::get(request()->input('hash'));
|
||||
|
||||
$this->invoice = Invoice::find($this->decodePrimaryKey($hash['invoice_id']));
|
||||
$this->invoice_terms = Invoice::find($this->decodePrimaryKey($hash['invoice_id']))->terms;
|
||||
}
|
||||
|
||||
count($this->fields) > 0 || $this->show_terms
|
||||
@ -164,6 +231,24 @@ class RequiredClientInfo extends Component
|
||||
: $this->show_form = false;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function contact()
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
return ClientContact::withTrashed()->find($this->contact_id);
|
||||
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function client()
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
return ClientContact::withTrashed()->find($this->contact_id)->client;
|
||||
|
||||
}
|
||||
|
||||
public function toggleTermsAccepted()
|
||||
{
|
||||
$this->terms_accepted = !$this->terms_accepted;
|
||||
@ -171,6 +256,10 @@ class RequiredClientInfo extends Component
|
||||
|
||||
public function handleSubmit(array $data): bool
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
$contact = ClientContact::withTrashed()->find($this->contact_id);
|
||||
|
||||
$rules = [];
|
||||
|
||||
collect($this->fields)->map(function ($field) use (&$rules) {
|
||||
@ -192,7 +281,7 @@ class RequiredClientInfo extends Component
|
||||
if ($this->updateClientDetails($data)) {
|
||||
$this->dispatch(
|
||||
'passed-required-fields-check',
|
||||
client_postal_code: $this->contact->client->postal_code
|
||||
client_postal_code: $contact->client->postal_code
|
||||
);
|
||||
|
||||
//if stripe is enabled, we want to update the customer at this point.
|
||||
@ -209,6 +298,11 @@ class RequiredClientInfo extends Component
|
||||
$client = [];
|
||||
$contact = [];
|
||||
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
$_contact = ClientContact::withTrashed()->find($this->contact_id);
|
||||
|
||||
|
||||
foreach ($data as $field => $value) {
|
||||
if (Str::startsWith($field, 'client_')) {
|
||||
$client[$this->mappings[$field]] = $value;
|
||||
@ -219,20 +313,43 @@ class RequiredClientInfo extends Component
|
||||
}
|
||||
}
|
||||
|
||||
$contact_update = $this->contact
|
||||
|
||||
$_contact->first_name = $this->contact_first_name;
|
||||
$_contact->last_name = $this->contact_last_name;
|
||||
$_contact->client->name = $this->client_name;
|
||||
$_contact->email = $this->contact_email;
|
||||
$_contact->client->phone = $this->client_phone;
|
||||
$_contact->client->address1 = $this->client_address_line_1;
|
||||
$_contact->client->city = $this->client_city;
|
||||
$_contact->client->state = $this->client_state;
|
||||
$_contact->client->country_id = $this->client_country_id;
|
||||
$_contact->client->postal_code = $this->client_postal_code;
|
||||
$_contact->client->shipping_address1 = $this->client_shipping_address_line_1;
|
||||
$_contact->client->shipping_city = $this->client_shipping_city;
|
||||
$_contact->client->shipping_state = $this->client_shipping_state;
|
||||
$_contact->client->shipping_postal_code = $this->client_shipping_postal_code;
|
||||
$_contact->client->shipping_country_id = $this->client_shipping_country_id;
|
||||
$_contact->client->custom_value1 = $this->client_custom_value1;
|
||||
$_contact->client->custom_value2 = $this->client_custom_value2;
|
||||
$_contact->client->custom_value3 = $this->client_custom_value3;
|
||||
$_contact->client->custom_value4 = $this->client_custom_value4;
|
||||
$_contact->push();
|
||||
|
||||
|
||||
$contact_update = $_contact
|
||||
->fill($contact)
|
||||
->push();
|
||||
|
||||
$client_update = $this->contact->client
|
||||
$client_update = $_contact->client
|
||||
->fill($client)
|
||||
->push();
|
||||
|
||||
if ($contact_update && $client_update) {
|
||||
if ($_contact) {
|
||||
/** @var \App\Models\CompanyGateway $cg */
|
||||
$cg = CompanyGateway::find($this->company_gateway_id);
|
||||
|
||||
if ($cg && $cg->update_details) {
|
||||
$payment_gateway = $cg->driver($this->client)->init();
|
||||
$payment_gateway = $cg->driver($_contact->client)->init();
|
||||
|
||||
if (method_exists($payment_gateway, "updateCustomer")) {
|
||||
$payment_gateway->updateCustomer();
|
||||
@ -247,11 +364,15 @@ class RequiredClientInfo extends Component
|
||||
|
||||
public function checkFields()
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
$_contact = ClientContact::withTrashed()->find($this->contact_id);
|
||||
|
||||
foreach ($this->fields as $index => $field) {
|
||||
$_field = $this->mappings[$field['name']];
|
||||
|
||||
if (Str::startsWith($field['name'], 'client_')) {
|
||||
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || in_array($_field, $this->client_address_array)) {
|
||||
if (empty($_contact->client->{$_field}) || is_null($_contact->client->{$_field}) || in_array($_field, $this->client_address_array)) {
|
||||
$this->show_form = true;
|
||||
} else {
|
||||
$this->fields[$index]['filled'] = true;
|
||||
@ -259,7 +380,7 @@ class RequiredClientInfo extends Component
|
||||
}
|
||||
|
||||
if (Str::startsWith($field['name'], 'contact_')) {
|
||||
if (empty($this->contact->{$_field}) || is_null($this->contact->{$_field}) || str_contains($this->contact->{$_field}, '@example.com')) {
|
||||
if (empty($_contact->{$_field}) || is_null($_contact->{$_field}) || str_contains($_contact->{$_field}, '@example.com')) {
|
||||
$this->show_form = true;
|
||||
} else {
|
||||
$this->fields[$index]['filled'] = true;
|
||||
@ -289,14 +410,18 @@ class RequiredClientInfo extends Component
|
||||
|
||||
public function handleCopyBilling(): void
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
$_contact = ClientContact::withTrashed()->find($this->contact_id);
|
||||
|
||||
$this->dispatch(
|
||||
'update-shipping-data',
|
||||
client_shipping_address_line_1: $this->contact->client->address1,
|
||||
client_shipping_address_line_2: $this->contact->client->address2,
|
||||
client_shipping_city: $this->contact->client->city,
|
||||
client_shipping_state: $this->contact->client->state,
|
||||
client_shipping_postal_code: $this->contact->client->postal_code,
|
||||
client_shipping_country_id: $this->contact->client->country_id,
|
||||
client_shipping_address_line_1: $_contact->client->address1,
|
||||
client_shipping_address_line_2: $_contact->client->address2,
|
||||
client_shipping_city: $_contact->client->city,
|
||||
client_shipping_state: $_contact->client->state,
|
||||
client_shipping_postal_code: $_contact->client->postal_code,
|
||||
client_shipping_country_id: $_contact->client->country_id,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ class ClientStatement extends Mailable
|
||||
public function content()
|
||||
{
|
||||
return new Content(
|
||||
view: $this->data['company']->account->isPremium() ? 'email.template.client_premium' : 'email.template.client',
|
||||
view: 'email.template.client',
|
||||
text: 'email.template.text',
|
||||
with: [
|
||||
'text_body' => $this->data['body'],
|
||||
|
@ -75,7 +75,8 @@ class TemplateEmail extends Mailable
|
||||
$template_name = 'email.template.'.$this->build_email->getTemplate();
|
||||
|
||||
if ($this->build_email->getTemplate() == 'light' || $this->build_email->getTemplate() == 'dark') {
|
||||
$template_name = $this->company->account->isPremium() ? 'email.template.client_premium' : 'email.template.client';
|
||||
// $template_name = $this->company->account->isPremium() ? 'email.template.client_premium' : 'email.template.client';
|
||||
$template_name = 'email.template.client';
|
||||
}
|
||||
|
||||
if ($this->build_email->getTemplate() == 'custom') {
|
||||
|
@ -72,7 +72,8 @@ class VendorTemplateEmail extends Mailable
|
||||
$template_name = 'email.template.'.$this->build_email->getTemplate();
|
||||
|
||||
if ($this->build_email->getTemplate() == 'light' || $this->build_email->getTemplate() == 'dark') {
|
||||
$template_name = $this->company->account->isPremium() ? 'email.template.client_premium' : 'email.template.client';
|
||||
// $template_name = $this->company->account->isPremium() ? 'email.template.client_premium' : 'email.template.client';
|
||||
$template_name = 'email.template.client';
|
||||
}
|
||||
|
||||
if ($this->build_email->getTemplate() == 'custom') {
|
||||
|
@ -208,6 +208,29 @@ class Document extends BaseModel
|
||||
return ctrans('texts.document');
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
$entity_id = $this->encodePrimaryKey($this->documentable_id);
|
||||
$link = '';
|
||||
|
||||
match($this->documentable_type) {
|
||||
'App\Models\Vendor' => $link = "/vendors/{$entity_id}",
|
||||
'App\Models\Project' => $link = "/projects/{$entity_id}",
|
||||
'invoices' => $link = "/invoices/{$entity_id}/edit",
|
||||
'App\Models\Quote' => $link = "/quotes/{$entity_id}/edit",
|
||||
'App\Models\Credit' => $link = "/credits/{$entity_id}/edit",
|
||||
'App\Models\Expense' => $link = "/expenses/{$entity_id}/edit",
|
||||
'App\Models\Payment' => $link = "/payments/{$entity_id}/edit",
|
||||
'App\Models\Task' => $link = "/tasks/{$entity_id}/edit",
|
||||
'App\Models\Client' => $link = "/clients/{$entity_id}",
|
||||
'App\Models\RecurringExpense' => $link = "/recurring_expenses/{$entity_id}/edit",
|
||||
'App\Models\RecurringInvoice' => $link = "/recurring_invoices/{$entity_id}/edit",
|
||||
default => $link = '',
|
||||
};
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
public function compress(): mixed
|
||||
{
|
||||
|
||||
|
@ -137,22 +137,22 @@ class Gateway extends StaticModel
|
||||
case 56:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'customer.source.updated','payment_intent.processing', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'customer.source.updated','payment_intent.processing', 'payment_intent.payment_failed', 'charge.failed']],
|
||||
GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => false, 'webhooks' => ['payment_intent.processing','payment_intent.succeeded','payment_intent.partially_funded', 'payment_intent.payment_failed']],
|
||||
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.processing', 'payment_intent.succeeded', 'mandate.updated', 'payment_intent.payment_failed']],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
||||
GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.processing', 'payment_intent.succeeded', 'mandate.updated', 'payment_intent.payment_failed']],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', ]],
|
||||
];
|
||||
case 39:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
|
||||
|
@ -355,11 +355,13 @@ class RecurringInvoice extends BaseModel
|
||||
public function calculateStatus(bool $new_model = false) //15-02-2024 - $new_model needed
|
||||
{
|
||||
|
||||
if($this->remaining_cycles == 0) {
|
||||
if($this->remaining_cycles == 0)
|
||||
return self::STATUS_COMPLETED;
|
||||
} elseif ($new_model && $this->status_id == self::STATUS_ACTIVE && Carbon::parse($this->next_send_date)->isFuture())
|
||||
elseif ($new_model && $this->status_id == self::STATUS_ACTIVE && Carbon::parse($this->next_send_date)->isFuture())
|
||||
return self::STATUS_PENDING;
|
||||
|
||||
elseif($this->remaining_cycles != 0 && ($this->status_id == self::STATUS_COMPLETED))
|
||||
return self::STATUS_ACTIVE;
|
||||
|
||||
return $this->status_id;
|
||||
|
||||
}
|
||||
|
@ -180,6 +180,7 @@ class Webhook extends BaseModel
|
||||
self::EVENT_DELETE_PURCHASE_ORDER,
|
||||
self::EVENT_RESTORE_PURCHASE_ORDER,
|
||||
self::EVENT_ARCHIVE_PURCHASE_ORDER,
|
||||
self::EVENT_CREATE_PRODUCT,
|
||||
self::EVENT_UPDATE_PRODUCT,
|
||||
self::EVENT_DELETE_PRODUCT,
|
||||
self::EVENT_RESTORE_PRODUCT,
|
||||
|
@ -271,7 +271,7 @@ class CreditCard implements MethodInterface
|
||||
$errors = $api_response->getErrors();
|
||||
}
|
||||
|
||||
if (property_exists($customers, 'customers')) {
|
||||
if ($customers && property_exists($customers, 'customers')) {
|
||||
return $customers->customers[0]->id;
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,10 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
}
|
||||
|
||||
$company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
|
||||
|
||||
if(!$company_gateway)
|
||||
return;
|
||||
|
||||
$stripe_driver = $company_gateway->driver()->init();
|
||||
|
||||
$charge_id = false;
|
||||
|
@ -44,7 +44,6 @@ class InstantPayment
|
||||
|
||||
public function run()
|
||||
{
|
||||
nlog($this->request->all());
|
||||
|
||||
$cc = auth()->guard('contact')->user();
|
||||
|
||||
|
@ -248,6 +248,12 @@ class Email implements ShouldQueue
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function incrementEmailCounter(): void
|
||||
{
|
||||
if (in_array($this->mailer, ['default', 'mailgun']))
|
||||
Cache::increment("email_quota" . $this->company->account->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to send the email
|
||||
*
|
||||
@ -279,7 +285,7 @@ class Email implements ShouldQueue
|
||||
|
||||
$mailer->send($this->mailable);
|
||||
|
||||
Cache::increment("email_quota" . $this->company->account->key);
|
||||
$this->incrementEmailCounter();
|
||||
|
||||
LightLogs::create(new EmailSuccess($this->company->company_key, $this->mailable->subject))
|
||||
->send();
|
||||
@ -501,7 +507,7 @@ class Email implements ShouldQueue
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
if(Ninja::isHosted() && $this->company->account->isPaid() && $this->email_object->settings->email_sending_method == 'default') {
|
||||
if (Ninja::isHosted() && $this->company->account->isPaid() && $this->email_object->settings->email_sending_method == 'default') {
|
||||
|
||||
try {
|
||||
|
||||
@ -510,17 +516,24 @@ class Email implements ShouldQueue
|
||||
$domain = explode("@", $email)[1] ?? "";
|
||||
$dns = dns_get_record($domain, DNS_MX);
|
||||
$server = $dns[0]["target"];
|
||||
if(stripos($server, "outlook.com") !== false) {
|
||||
if (stripos($server, "outlook.com") !== false) {
|
||||
|
||||
if (property_exists($this->email_object->settings, 'email_from_name') && strlen($this->email_object->settings->email_from_name) > 1) {
|
||||
$email_from_name = $this->email_object->settings->email_from_name;
|
||||
} else {
|
||||
$email_from_name = $this->company->present()->name();
|
||||
}
|
||||
|
||||
$this->mailer = 'postmark';
|
||||
$this->client_postmark_secret = config('services.postmark-outlook.token');
|
||||
$this->mailable
|
||||
->from('maildelivery@invoice.services', 'Invoice Ninja');
|
||||
->from(config('services.postmark-outlook.from.address'), $email_from_name);
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
nlog("problem switching outlook driver - hosted");
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
}
|
||||
@ -551,11 +564,10 @@ class Email implements ShouldQueue
|
||||
$this->mailer = 'mailgun';
|
||||
$this->setMailgunMailer();
|
||||
return $this;
|
||||
case 'client_brevo':
|
||||
$this->mailer = 'brevo';
|
||||
$this->setBrevoMailer();
|
||||
case 'smtp':
|
||||
$this->mailer = 'smtp';
|
||||
$this->configureSmtpMailer();
|
||||
return $this;
|
||||
|
||||
default:
|
||||
$this->mailer = config('mail.default');
|
||||
return $this;
|
||||
@ -568,6 +580,43 @@ class Email implements ShouldQueue
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function configureSmtpMailer(): void
|
||||
{
|
||||
|
||||
$company = $this->company;
|
||||
|
||||
$smtp_host = $company->smtp_host;
|
||||
$smtp_port = $company->smtp_port;
|
||||
$smtp_username = $company->smtp_username;
|
||||
$smtp_password = $company->smtp_password;
|
||||
$smtp_encryption = $company->smtp_encryption ?? 'tls';
|
||||
$smtp_local_domain = strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null;
|
||||
$smtp_verify_peer = $company->smtp_verify_peer ?? true;
|
||||
|
||||
config([
|
||||
'mail.mailers.smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'host' => $smtp_host,
|
||||
'port' => $smtp_port,
|
||||
'username' => $smtp_username,
|
||||
'password' => $smtp_password,
|
||||
'encryption' => $smtp_encryption,
|
||||
'local_domain' => $smtp_local_domain,
|
||||
'verify_peer' => $smtp_verify_peer,
|
||||
'timeout' => 30,
|
||||
],
|
||||
]);
|
||||
|
||||
$user = $this->resolveSendingUser();
|
||||
|
||||
$sending_email = (isset ($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset ($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
|
||||
$this->mailable
|
||||
->from($sending_email, $sending_user);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration of multiple mailers
|
||||
* per company for use by self hosted users
|
||||
@ -668,8 +717,8 @@ class Email implements ShouldQueue
|
||||
|
||||
$user = $this->resolveSendingUser();
|
||||
|
||||
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
$sending_email = (isset ($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset ($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
|
||||
$this->mailable
|
||||
->from($sending_email, $sending_user);
|
||||
@ -690,8 +739,8 @@ class Email implements ShouldQueue
|
||||
|
||||
$user = $this->resolveSendingUser();
|
||||
|
||||
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
$sending_email = (isset ($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset ($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
|
||||
$this->mailable
|
||||
->from($sending_email, $sending_user);
|
||||
@ -712,8 +761,8 @@ class Email implements ShouldQueue
|
||||
|
||||
$user = $this->resolveSendingUser();
|
||||
|
||||
$sending_email = (isset($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
$sending_email = (isset ($this->email_object->settings->custom_sending_email) && stripos($this->email_object->settings->custom_sending_email, "@")) ? $this->email_object->settings->custom_sending_email : $user->email;
|
||||
$sending_user = (isset ($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
|
||||
|
||||
$this->mailable
|
||||
->from($sending_email, $sending_user);
|
||||
|
@ -107,10 +107,10 @@ class EmailDefaults
|
||||
|
||||
match ($this->email->email_object->settings->email_style) {
|
||||
'plain' => $this->template = 'email.template.plain',
|
||||
'light' => $this->template = $this->email->email_object->company->account->isPremium() ? 'email.template.client_premium' : 'email.template.client',
|
||||
'dark' => $this->template = $this->email->email_object->company->account->isPremium() ? 'email.template.client_premium' :'email.template.client',
|
||||
'light' => $this->template = 'email.template.client',
|
||||
'dark' => $this->template = 'email.template.client',
|
||||
'custom' => $this->template = 'email.template.custom',
|
||||
default => $this->template = $this->email->email_object->company->account->isPremium() ? 'email.template.client_premium' :'email.template.client',
|
||||
default => $this->template = 'email.template.client',
|
||||
};
|
||||
|
||||
$this->email->email_object->html_template = $this->template;
|
||||
@ -123,7 +123,7 @@ class EmailDefaults
|
||||
*/
|
||||
private function setFrom(): self
|
||||
{
|
||||
if (Ninja::isHosted() && $this->email->email_object->settings->email_sending_method == 'default') {
|
||||
if (Ninja::isHosted() && in_array($this->email->email_object->settings->email_sending_method,['default', 'mailgun'])) {
|
||||
if ($this->email->company->account->isPaid() && property_exists($this->email->email_object->settings, 'email_from_name') && strlen($this->email->email_object->settings->email_from_name) > 1) {
|
||||
$email_from_name = $this->email->email_object->settings->email_from_name;
|
||||
} else {
|
||||
|
@ -90,7 +90,7 @@ class InvoiceService
|
||||
if ($company_currency != $client_currency) {
|
||||
$exchange_rate = new CurrencyApi();
|
||||
|
||||
$this->invoice->exchange_rate = $exchange_rate->exchangeRate($client_currency, $company_currency, now());
|
||||
$this->invoice->exchange_rate = 1/$exchange_rate->exchangeRate($client_currency, $company_currency, now());
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -90,6 +90,9 @@ class ARDetailReport extends BaseExport
|
||||
|
||||
$query = Invoice::query()
|
||||
->withTrashed()
|
||||
->whereHas('client', function ($query){
|
||||
$query->where('is_deleted', 0);
|
||||
})
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->where('balance', '>', 0)
|
||||
|
@ -125,9 +125,9 @@ class ARSummaryReport extends BaseExport
|
||||
$amount = Invoice::withTrashed()
|
||||
->where('client_id', $this->client->id)
|
||||
->where('company_id', $this->client->company_id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->where('is_deleted', 0)
|
||||
->where(function ($query) {
|
||||
$query->where('due_date', '>', now()->startOfDay())
|
||||
->orWhereNull('due_date');
|
||||
|
@ -25,6 +25,8 @@ use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Csv\Writer;
|
||||
|
||||
use function Sentry\continueTrace;
|
||||
|
||||
class ProfitLoss
|
||||
{
|
||||
private bool $is_income_billed = true;
|
||||
@ -280,27 +282,36 @@ class ProfitLoss
|
||||
$tax_amount_credit = 0;
|
||||
$tax_amount_credit_converted = $tax_amount_credit_converted = 0;
|
||||
|
||||
$invoice = false;
|
||||
|
||||
foreach ($payment->paymentables as $pivot) {
|
||||
if ($pivot->paymentable_type == 'invoices') {
|
||||
$invoice = Invoice::query()->withTrashed()->find($pivot->paymentable_id);
|
||||
|
||||
if(!$invoice)
|
||||
continue;
|
||||
|
||||
$pivot_diff = $pivot->amount - $pivot->refunded;
|
||||
$amount_payment_paid += $pivot_diff;
|
||||
$amount_payment_paid_converted += $pivot_diff / ($payment->exchange_rate ?: 1);
|
||||
$amount_payment_paid_converted += $pivot_diff * ($payment->exchange_rate ?: 1);
|
||||
|
||||
if ($invoice->amount > 0) {
|
||||
$tax_amount += ($pivot_diff / $invoice->amount) * $invoice->total_taxes;
|
||||
$tax_amount_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate;
|
||||
$tax_amount_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $invoice->exchange_rate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!$invoice) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($pivot->paymentable_type == 'credits') {
|
||||
$amount_credit_paid += $pivot->amount - $pivot->refunded;
|
||||
$amount_credit_paid_converted += $pivot_diff / ($payment->exchange_rate ?: 1);
|
||||
$amount_credit_paid_converted += $pivot_diff * ($payment->exchange_rate ?: 1);
|
||||
|
||||
$tax_amount_credit += ($pivot_diff / $invoice->amount) * $invoice->total_taxes;
|
||||
$tax_amount_credit_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate;
|
||||
$tax_amount_credit_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $invoice->exchange_rate;
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,6 +351,10 @@ class ProfitLoss
|
||||
*/
|
||||
public function getCsv()
|
||||
{
|
||||
nlog($this->income);
|
||||
nlog($this->income_taxes);
|
||||
nlog(array_sum(array_column($this->expense_break_down, 'total')));
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
App::forgetInstance('translator');
|
||||
App::setLocale($this->company->locale());
|
||||
@ -356,7 +371,7 @@ class ProfitLoss
|
||||
|
||||
$csv->insertOne(['--------------------']);
|
||||
|
||||
$csv->insertOne([ctrans('texts.total_revenue'), Number::formatMoney($this->income, $this->company)]);
|
||||
$csv->insertOne([ctrans('texts.total_revenue'). "[".ctrans('texts.tax')." " .ctrans('texts.exclusive'). "]", Number::formatMoney($this->income, $this->company)]);
|
||||
|
||||
//total taxes
|
||||
|
||||
@ -371,12 +386,12 @@ class ProfitLoss
|
||||
//total expense taxes
|
||||
|
||||
$csv->insertOne(['--------------------']);
|
||||
$csv->insertOne([ctrans('texts.total_expenses'), Number::formatMoney(array_sum(array_column($this->expense_break_down, 'total')), $this->company)]);
|
||||
$csv->insertOne([ctrans('texts.total_expenses'). "[".ctrans('texts.tax')." " .ctrans('texts.exclusive'). "]", Number::formatMoney(array_sum(array_column($this->expense_break_down, 'total')), $this->company)]);
|
||||
|
||||
$csv->insertOne([ctrans('texts.total_taxes'), Number::formatMoney(array_sum(array_column($this->expense_break_down, 'tax')), $this->company)]);
|
||||
|
||||
$csv->insertOne(['--------------------']);
|
||||
$csv->insertOne([ctrans('texts.total_profit'), Number::formatMoney($this->income - $this->income_taxes - array_sum(array_column($this->expense_break_down, 'total')) - array_sum(array_column($this->expense_break_down, 'tax')), $this->company)]);
|
||||
$csv->insertOne([ctrans('texts.total_profit'), Number::formatMoney($this->income - array_sum(array_column($this->expense_break_down, 'total')), $this->company)]);
|
||||
|
||||
//net profit
|
||||
|
||||
@ -384,11 +399,25 @@ class ProfitLoss
|
||||
$csv->insertOne(['']);
|
||||
$csv->insertOne(['']);
|
||||
|
||||
|
||||
$csv->insertOne(['--------------------']);
|
||||
$csv->insertOne([ctrans('texts.revenue')]);
|
||||
$csv->insertOne(['--------------------']);
|
||||
|
||||
$csv->insertOne([ctrans('texts.currency'), ctrans('texts.amount'), ctrans('texts.total_taxes')]);
|
||||
foreach ($this->foreign_income as $foreign_income) {
|
||||
$csv->insertOne([$foreign_income['currency'], ($foreign_income['amount'] - $foreign_income['total_taxes']), $foreign_income['total_taxes']]);
|
||||
}
|
||||
|
||||
$csv->insertOne(['']);
|
||||
$csv->insertOne(['']);
|
||||
$csv->insertOne(['--------------------']);
|
||||
$csv->insertOne([ctrans('texts.expenses')]);
|
||||
$csv->insertOne(['--------------------']);
|
||||
foreach($this->expenses as $expense){
|
||||
$csv->insertOne([$expense->currency, ($expense->total - $expense->foreign_tax_amount), $expense->foreign_tax_amount]);
|
||||
}
|
||||
|
||||
return $csv->toString();
|
||||
}
|
||||
|
||||
@ -421,6 +450,11 @@ class ProfitLoss
|
||||
private function expenseData()
|
||||
{
|
||||
$expenses = Expense::query()->where('company_id', $this->company->id)
|
||||
->where(function ($query){
|
||||
$query->whereNull('client_id')->orWhereHas('client', function ($q){
|
||||
$q->where('is_deleted', 0);
|
||||
});
|
||||
})
|
||||
->where('is_deleted', 0)
|
||||
->withTrashed()
|
||||
->whereBetween('date', [$this->start_date, $this->end_date])
|
||||
@ -428,19 +462,21 @@ class ProfitLoss
|
||||
|
||||
$this->expenses = [];
|
||||
|
||||
$company_currency_code = $this->company->currency()->code;
|
||||
|
||||
foreach ($expenses as $expense) {
|
||||
$map = new \stdClass();
|
||||
|
||||
$amount = $expense->amount;
|
||||
|
||||
$expense_tax_total = $this->getTax($expense);
|
||||
$map->total = $expense->amount;
|
||||
$map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate);
|
||||
$map->tax = $tax = $this->getTax($expense);
|
||||
$map->net_converted_total = $expense->uses_inclusive_taxes ? ($converted_total - $tax) : $converted_total;
|
||||
$map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate); //converted to company currency
|
||||
$map->tax = $tax = $this->getConvertedTotal($expense_tax_total, $expense->exchange_rate); //tax component
|
||||
$map->net_converted_total = $expense->uses_inclusive_taxes ? ($converted_total - $tax) : $converted_total; //excludes all taxes
|
||||
$map->category_id = $expense->category_id;
|
||||
$map->category_name = $expense->category ? $expense->category->name : 'No Category Defined';
|
||||
$map->currency_id = $expense->currency_id ?: $expense->company->settings->currency_id;
|
||||
|
||||
$map->currency = $expense->currency ? $expense->currency->code : $company_currency_code;
|
||||
$map->foreign_tax_amount = $expense_tax_total;
|
||||
$this->expenses[] = $map;
|
||||
}
|
||||
|
||||
@ -480,10 +516,6 @@ class ProfitLoss
|
||||
//is amount tax
|
||||
|
||||
if ($expense->calculate_tax_by_amount) {
|
||||
nlog($expense->tax_amount1);
|
||||
nlog($expense->tax_amount2);
|
||||
nlog($expense->tax_amount3);
|
||||
|
||||
return $expense->tax_amount1 + $expense->tax_amount2 + $expense->tax_amount3;
|
||||
}
|
||||
|
||||
|
@ -75,8 +75,8 @@ class TaxSummaryReport extends BaseExport
|
||||
|
||||
$query = Invoice::query()
|
||||
->withTrashed()
|
||||
->whereIn('status_id', [2,3,4])
|
||||
->where('company_id', $this->company->id)
|
||||
->whereIn('status_id', [2,3,4])
|
||||
->where('is_deleted', 0)
|
||||
->orderBy('balance', 'desc');
|
||||
|
||||
|
@ -11,34 +11,35 @@
|
||||
|
||||
namespace App\Services\Scheduler;
|
||||
|
||||
use App\Export\CSV\ClientExport;
|
||||
use App\Export\CSV\ContactExport;
|
||||
use App\Export\CSV\CreditExport;
|
||||
use App\Export\CSV\DocumentExport;
|
||||
use App\Export\CSV\ExpenseExport;
|
||||
use App\Export\CSV\InvoiceExport;
|
||||
use App\Export\CSV\InvoiceItemExport;
|
||||
use App\Export\CSV\PaymentExport;
|
||||
use App\Export\CSV\ProductExport;
|
||||
use App\Export\CSV\ProductSalesExport;
|
||||
use App\Export\CSV\QuoteExport;
|
||||
use App\Export\CSV\QuoteItemExport;
|
||||
use App\Export\CSV\RecurringInvoiceExport;
|
||||
use App\Export\CSV\TaskExport;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Mail\DownloadReport;
|
||||
use App\Models\Client;
|
||||
use App\Models\Scheduler;
|
||||
use App\Mail\DownloadReport;
|
||||
use App\Export\CSV\TaskExport;
|
||||
use App\Export\CSV\QuoteExport;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Export\CSV\ClientExport;
|
||||
use App\Export\CSV\CreditExport;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Export\CSV\ContactExport;
|
||||
use App\Export\CSV\ExpenseExport;
|
||||
use App\Export\CSV\InvoiceExport;
|
||||
use App\Export\CSV\PaymentExport;
|
||||
use App\Export\CSV\ProductExport;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Export\CSV\ActivityExport;
|
||||
use App\Export\CSV\DocumentExport;
|
||||
use App\Export\CSV\QuoteItemExport;
|
||||
use App\Services\Report\ProfitLoss;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Export\CSV\InvoiceItemExport;
|
||||
use App\Export\CSV\ProductSalesExport;
|
||||
use App\Services\Report\ARDetailReport;
|
||||
use App\Services\Report\ARSummaryReport;
|
||||
use App\Services\Report\ClientBalanceReport;
|
||||
use App\Services\Report\ClientSalesReport;
|
||||
use App\Services\Report\ProfitLoss;
|
||||
use App\Services\Report\TaxSummaryReport;
|
||||
use App\Services\Report\UserSalesReport;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Services\Report\TaxSummaryReport;
|
||||
use App\Export\CSV\RecurringInvoiceExport;
|
||||
use App\Services\Report\ClientSalesReport;
|
||||
use App\Services\Report\ClientBalanceReport;
|
||||
|
||||
class EmailReport
|
||||
{
|
||||
@ -77,19 +78,33 @@ class EmailReport
|
||||
'client_sales' => $export = (new ClientSalesReport($this->scheduler->company, $data)),
|
||||
'user_sales' => $export = (new UserSalesReport($this->scheduler->company, $data)),
|
||||
'profitloss' => $export = (new ProfitLoss($this->scheduler->company, $data)),
|
||||
'activity' => $export = (new ActivityExport($this->scheduler->company, $data)),
|
||||
'activities' => $export = (new ActivityExport($this->scheduler->company, $data)),
|
||||
'client' => $export = (new ClientExport($this->scheduler->company, $data)),
|
||||
'clients' => $export = (new ClientExport($this->scheduler->company, $data)),
|
||||
'client_contact' => $export = (new ContactExport($this->scheduler->company, $data)),
|
||||
'client_contacts' => $export = (new ContactExport($this->scheduler->company, $data)),
|
||||
'credit' => $export = (new CreditExport($this->scheduler->company, $data)),
|
||||
'credits' => $export = (new CreditExport($this->scheduler->company, $data)),
|
||||
'document' => $export = (new DocumentExport($this->scheduler->company, $data)),
|
||||
'documents' => $export = (new DocumentExport($this->scheduler->company, $data)),
|
||||
'expense' => $export = (new ExpenseExport($this->scheduler->company, $data)),
|
||||
'expenses' => $export = (new ExpenseExport($this->scheduler->company, $data)),
|
||||
'invoice' => $export = (new InvoiceExport($this->scheduler->company, $data)),
|
||||
'invoices' => $export = (new InvoiceExport($this->scheduler->company, $data)),
|
||||
'invoice_item' => $export = (new InvoiceItemExport($this->scheduler->company, $data)),
|
||||
'invoice_items' => $export = (new InvoiceItemExport($this->scheduler->company, $data)),
|
||||
'quote' => $export = (new QuoteExport($this->scheduler->company, $data)),
|
||||
'quotes' => $export = (new QuoteExport($this->scheduler->company, $data)),
|
||||
'quote_item' => $export = (new QuoteItemExport($this->scheduler->company, $data)),
|
||||
'quote_items' => $export = (new QuoteItemExport($this->scheduler->company, $data)),
|
||||
'recurring_invoice' => $export = (new RecurringInvoiceExport($this->scheduler->company, $data)),
|
||||
'recurring_invoices' => $export = (new RecurringInvoiceExport($this->scheduler->company, $data)),
|
||||
'payment' => $export = (new PaymentExport($this->scheduler->company, $data)),
|
||||
'payments' => $export = (new PaymentExport($this->scheduler->company, $data)),
|
||||
'product' => $export = (new ProductExport($this->scheduler->company, $data)),
|
||||
'task' => $export = (new TaskExport($this->scheduler->company, $data)),
|
||||
'products' => $export = (new ProductExport($this->scheduler->company, $data)),
|
||||
'tasks' => $export = (new TaskExport($this->scheduler->company, $data)),
|
||||
default => $export = false,
|
||||
};
|
||||
|
||||
|
@ -717,7 +717,7 @@ class TemplateService
|
||||
return collect($payment->refund_meta)
|
||||
->map(function ($refund) use ($payment) {
|
||||
|
||||
$date = \Carbon\Carbon::parse($refund['date'])->addSeconds($payment->client->timezone_offset());
|
||||
$date = \Carbon\Carbon::parse($refund['date'] ?? $payment->date)->addSeconds($payment->client->timezone_offset());
|
||||
$date = $this->translateDate($date, $payment->client->date_format(), $payment->client->locale());
|
||||
$entity = ctrans('texts.invoice');
|
||||
|
||||
@ -1032,6 +1032,8 @@ class TemplateService
|
||||
'payment_balance' => $purchase_order->client->payment_balance,
|
||||
'credit_balance' => $purchase_order->client->credit_balance,
|
||||
'vat_number' => $purchase_order->client->vat_number ?? '',
|
||||
'address' => $purchase_order->client->present()->address(),
|
||||
'shipping_address' => $purchase_order->client->present()->shipping_address(),
|
||||
] : [],
|
||||
'status_id' => (string)($purchase_order->status_id ?: 1),
|
||||
'status' => PurchaseOrder::stringStatus($purchase_order->status_id ?? 1),
|
||||
|
@ -132,6 +132,8 @@ class CreditTransformer extends EntityTransformer
|
||||
'paid_to_date' => (float) $credit->paid_to_date,
|
||||
'subscription_id' => $this->encodePrimaryKey($credit->subscription_id),
|
||||
'invoice_id' => $credit->invoice_id ? $this->encodePrimaryKey($credit->invoice_id) : '',
|
||||
'tax_info' => $credit->tax_data ?: new \stdClass(),
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ class DocumentTransformer extends EntityTransformer
|
||||
'created_at' => (int) $document->created_at,
|
||||
'is_deleted' => (bool) false,
|
||||
'is_public' => (bool) $document->is_public,
|
||||
'link' => (string) $document->link(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,12 @@ class ProjectTransformer extends EntityTransformer
|
||||
|
||||
public function includeClient(Project $project): \League\Fractal\Resource\Item
|
||||
{
|
||||
|
||||
if (!$project->client) {
|
||||
nlog("Project {$project->hashed_id} does not have a client attached - this project is in a bad state");
|
||||
return null;
|
||||
}
|
||||
|
||||
$transformer = new ClientTransformer($this->serializer);
|
||||
|
||||
return $this->includeItem($project->client, $transformer, Client::class);
|
||||
|
@ -149,6 +149,7 @@ class PurchaseOrderTransformer extends EntityTransformer
|
||||
'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id),
|
||||
'expense_id' => $this->encodePrimaryKey($purchase_order->expense_id),
|
||||
'currency_id' => $purchase_order->currency_id ? (string) $purchase_order->currency_id : '',
|
||||
'tax_info' => $purchase_order->tax_data ?: new \stdClass(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ class QuoteTransformer extends EntityTransformer
|
||||
'paid_to_date' => (float) $quote->paid_to_date,
|
||||
'project_id' => $this->encodePrimaryKey($quote->project_id),
|
||||
'subscription_id' => $this->encodePrimaryKey($quote->subscription_id),
|
||||
|
||||
'tax_info' => $quote->tax_data ?: new \stdClass(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -397,7 +397,8 @@ class HtmlEngine
|
||||
$data['$credit.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.credit_date')];
|
||||
$data['$balance'] = ['value' => Number::formatMoney($this->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.balance')];
|
||||
$data['$credit.balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
|
||||
|
||||
$data['$client.credit_balance'] = &$data['$credit.balance'];
|
||||
|
||||
$data['$invoice.balance'] = &$data['$balance'];
|
||||
$data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: ' ', 'label' => ctrans('texts.taxes')];
|
||||
$data['$invoice.taxes'] = &$data['$taxes'];
|
||||
@ -621,6 +622,33 @@ class HtmlEngine
|
||||
$data['$task.task3'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'task3')];
|
||||
$data['$task.task4'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'task4')];
|
||||
|
||||
|
||||
if($this->entity->vendor) {
|
||||
|
||||
$data['$vendor_name'] = ['value' => $this->entity->vendor->present()->name() ?: ' ', 'label' => ctrans('texts.vendor_name')];
|
||||
$data['$vendor.name'] = &$data['$vendor_name'];
|
||||
$data['$vendor'] = &$data['$vendor_name'];
|
||||
$data['$vendor.address1'] = ['value' => $this->entity->vendor->address1 ?: ' ', 'label' => ctrans('texts.address1')];
|
||||
$data['$vendor.address2'] = ['value' => $this->entity->vendor->address2 ?: ' ', 'label' => ctrans('texts.address2')];
|
||||
$data['$vendor.id_number'] = ['value' => $this->entity->vendor->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
|
||||
$data['$vendor.number'] = ['value' => $this->entity->vendor->number ?: ' ', 'label' => ctrans('texts.number')];
|
||||
$data['$vendor.vat_number'] = ['value' => $this->entity->vendor->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
|
||||
$data['$vendor.website'] = ['value' => $this->entity->vendor->present()->website() ?: ' ', 'label' => ctrans('texts.website')];
|
||||
$data['$vendor.phone'] = ['value' => $this->entity->vendor->present()->phone() ?: ' ', 'label' => ctrans('texts.phone')];
|
||||
$data['$vendor.country'] = ['value' => isset($this->entity->vendor->country->name) ? ctrans('texts.country_' . $this->entity->vendor->country->name) : '', 'label' => ctrans('texts.country')];
|
||||
$data['$vendor.country_2'] = ['value' => isset($this->entity->vendor->country) ? $this->entity->vendor->country->iso_3166_2 : '', 'label' => ctrans('texts.country')];
|
||||
$data['$vendor_address'] = ['value' => $this->entity->vendor->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
|
||||
$data['$vendor.address'] = &$data['$vendor_address'];
|
||||
$data['$vendor.postal_code'] = ['value' => $this->entity->vendor->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
|
||||
$data['$vendor.public_notes'] = ['value' => $this->entity->vendor->public_notes ?: ' ', 'label' => ctrans('texts.notes')];
|
||||
$data['$vendor.city'] = ['value' => $this->entity->vendor->city ?: ' ', 'label' => ctrans('texts.city')];
|
||||
$data['$vendor.state'] = ['value' => $this->entity->vendor->state ?: ' ', 'label' => ctrans('texts.state')];
|
||||
$data['$vendor.city_state_postal'] = ['value' => $this->entity->vendor->present()->cityStateZip($this->entity->vendor->city, $this->entity->vendor->state, $this->entity->vendor->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
|
||||
$data['$vendor.postal_city_state'] = ['value' => $this->entity->vendor->present()->cityStateZip($this->entity->vendor->city, $this->entity->vendor->state, $this->entity->vendor->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
|
||||
$data['$vendor.postal_city'] = ['value' => $this->entity->vendor->present()->cityStateZip($this->entity->vendor->city, null, $this->entity->vendor->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city')];
|
||||
|
||||
}
|
||||
|
||||
if ($this->settings->signature_on_pdf) {
|
||||
$data['$contact.signature'] = ['value' => $this->invitation->signature_base64, 'label' => ctrans('texts.signature')];
|
||||
$data['$contact.signature_date'] = ['value' => $this->translateDate($this->invitation->signature_date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.date')];
|
||||
|
@ -86,6 +86,33 @@ class Number
|
||||
return rtrim(rtrim(number_format($value, $precision, $decimal, $thousand), '0'), $decimal);
|
||||
}
|
||||
|
||||
public static function parseFloat($value)
|
||||
{
|
||||
|
||||
if(!$value)
|
||||
return 0;
|
||||
|
||||
//remove everything except for numbers, decimals, commas and hyphens
|
||||
$value = preg_replace('/[^0-9.,-]+/', '', $value);
|
||||
|
||||
$decimal = strpos($value, '.');
|
||||
$comma = strpos($value, ',');
|
||||
|
||||
if($comma === false) //no comma must be a decimal number already
|
||||
return (float) $value;
|
||||
|
||||
if($decimal < $comma){ //decimal before a comma = euro
|
||||
$value = str_replace(['.',','], ['','.'], $value);
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
//comma first = traditional thousand separator
|
||||
$value = str_replace(',', '', $value);
|
||||
|
||||
return (float)$value;
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* Formats a given value based on the clients currency
|
||||
* BACK to a float.
|
||||
@ -93,8 +120,9 @@ class Number
|
||||
* @param string $value The formatted number to be converted back to float
|
||||
* @return float The formatted value
|
||||
*/
|
||||
public static function parseFloat($value)
|
||||
public static function parseFloatXX($value)
|
||||
{
|
||||
|
||||
if(!$value)
|
||||
return 0;
|
||||
|
||||
@ -103,17 +131,14 @@ class Number
|
||||
if(substr($value, 0,1) == '-')
|
||||
$multiplier = -1;
|
||||
|
||||
// convert "," to "."
|
||||
$s = str_replace(',', '.', $value);
|
||||
|
||||
// remove everything except numbers and dot "."
|
||||
$s = preg_replace("/[^0-9\.]/", '', $s);
|
||||
|
||||
if ($s < 1) {
|
||||
return (float) $s;
|
||||
}
|
||||
|
||||
// remove all separators from first part and keep the end
|
||||
$s = str_replace('.', '', substr($s, 0, -3)).substr($s, -3);
|
||||
|
||||
if($multiplier)
|
||||
@ -122,6 +147,52 @@ class Number
|
||||
return (float) $s;
|
||||
}
|
||||
|
||||
|
||||
//next iteration of float parsing
|
||||
public static function parseFloat2($value)
|
||||
{
|
||||
|
||||
if(!$value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//remove everything except for numbers, decimals, commas and hyphens
|
||||
$value = preg_replace('/[^0-9.,-]+/', '', $value);
|
||||
|
||||
$decimal = strpos($value, '.');
|
||||
$comma = strpos($value, ',');
|
||||
|
||||
//check the 3rd last character
|
||||
if(!in_array(substr($value, -3, 1), [".", ","])) {
|
||||
|
||||
if($comma && (substr($value, -3, 1) != ".")) {
|
||||
$value .= ".00";
|
||||
} elseif($decimal && (substr($value, -3, 1) != ",")) {
|
||||
$value .= ",00";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$decimal = strpos($value, '.');
|
||||
$comma = strpos($value, ',');
|
||||
|
||||
if($comma === false) { //no comma must be a decimal number already
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
if($decimal < $comma) { //decimal before a comma = euro
|
||||
$value = str_replace(['.',','], ['','.'], $value);
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
//comma first = traditional thousand separator
|
||||
$value = str_replace(',', '', $value);
|
||||
|
||||
return (float)$value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static function parseStringFloat($value)
|
||||
{
|
||||
$value = preg_replace('/[^0-9-.]+/', '', $value);
|
||||
|
@ -84,6 +84,7 @@ class SystemHealth
|
||||
'trailing_slash' => (bool) self::checkUrlState(),
|
||||
'file_permissions' => (string) self::checkFileSystem(),
|
||||
'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(),
|
||||
'api_version' => (string) config('ninja.app_version'),
|
||||
];
|
||||
}
|
||||
|
||||
|
833
composer.lock
generated
833
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -17,8 +17,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION', '5.8.26'),
|
||||
'app_tag' => env('APP_TAG', '5.8.26'),
|
||||
'app_version' => env('APP_VERSION', '5.8.37'),
|
||||
'app_tag' => env('APP_TAG', '5.8.37'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
@ -39,7 +39,10 @@ return [
|
||||
],
|
||||
|
||||
'postmark-outlook' => [
|
||||
'token' => env('POSTMARK_OUTLOOK_SECRET','')
|
||||
'token' => env('POSTMARK_OUTLOOK_SECRET',''),
|
||||
'from' => [
|
||||
'address' => env('POSTMARK_OUTLOOK_FROM_ADDRESS', '')
|
||||
],
|
||||
],
|
||||
|
||||
'microsoft' => [
|
||||
|
@ -19,7 +19,7 @@ return new class extends Migration
|
||||
$table->text('smtp_username')->nullable();
|
||||
$table->text('smtp_password')->nullable();
|
||||
$table->string('smtp_local_domain')->nullable();
|
||||
$table->boolean('smtp_verify_peer')->default(0);
|
||||
$table->boolean('smtp_verify_peer')->default(true);
|
||||
});
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user