diff --git a/VERSION.txt b/VERSION.txt
index 36ed3dba85..ab8eeed0b5 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-5.10.26
\ No newline at end of file
+5.10.27
\ No newline at end of file
diff --git a/app/DataMapper/EmailTemplateDefaults.php b/app/DataMapper/EmailTemplateDefaults.php
index a63b7f8901..0d34fcfe48 100644
--- a/app/DataMapper/EmailTemplateDefaults.php
+++ b/app/DataMapper/EmailTemplateDefaults.php
@@ -140,7 +140,7 @@ class EmailTemplateDefaults
public static function emailPaymentFailedTemplate()
{
- return '
$client
'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'
$gateway_payment_error
$payment_button
';
+ return '$client
'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'
$payment_error
$payment_button
';
}
public static function emailQuoteReminder1Subject()
@@ -151,7 +151,7 @@ class EmailTemplateDefaults
public static function emailQuoteReminder1Body()
{
- return '$client
'.self::transformText('quote_reminder_message').'
$view_button
';
+ return '$client
'.self::transformText('quote_reminder_message').'
$view_button
';
}
@@ -177,14 +177,14 @@ class EmailTemplateDefaults
public static function emailInvoiceTemplate()
{
- $invoice_message = '$client
'.self::transformText('invoice_message').'
$view_button
';
+ $invoice_message = '$client
'.self::transformText('invoice_message').'
$view_button
';
return $invoice_message;
}
public static function emailInvoiceReminderTemplate()
{
- $invoice_message = '$client
'.self::transformText('reminder_message').'
$view_button
';
+ $invoice_message = '$client
'.self::transformText('reminder_message').'
$view_button
';
return $invoice_message;
}
@@ -196,7 +196,7 @@ class EmailTemplateDefaults
public static function emailQuoteTemplate()
{
- $quote_message = '$client
'.self::transformText('quote_message').'
$view_button
';
+ $quote_message = '$client
'.self::transformText('quote_message').'
$view_button
';
return $quote_message;
}
@@ -213,28 +213,28 @@ class EmailTemplateDefaults
public static function emailPurchaseOrderTemplate()
{
- $purchase_order_message = '$vendor
'.self::transformText('purchase_order_message').'
$view_button
';
+ $purchase_order_message = '$vendor
'.self::transformText('purchase_order_message').'
$view_button
';
return $purchase_order_message;
}
public static function emailPaymentTemplate()
{
- $payment_message = '$client
'.self::transformText('payment_message').'
$invoices
$view_button
';
+ $payment_message = '$client
'.self::transformText('payment_message').'
$invoices
$view_button
';
return $payment_message;
}
public static function emailCreditTemplate()
{
- $credit_message = '$client
'.self::transformText('credit_message').'
$view_button
';
+ $credit_message = '$client
'.self::transformText('credit_message').'
$view_button
';
return $credit_message;
}
public static function emailPaymentPartialTemplate()
{
- $payment_message = '$client
'.self::transformText('payment_message').'
$invoices
$view_button
';
+ $payment_message = '$client
'.self::transformText('payment_message').'
$invoices
$view_button
';
return $payment_message;
}
diff --git a/app/Filters/InvoiceFilters.php b/app/Filters/InvoiceFilters.php
index e31cfd0dec..9f3a935146 100644
--- a/app/Filters/InvoiceFilters.php
+++ b/app/Filters/InvoiceFilters.php
@@ -266,58 +266,6 @@ class InvoiceFilters extends QueryFilters
return $this->builder->where('due_date', '>=', $date);
}
- /**
- * Filter by date range
- *
- * @param string $date_range
- * @return Builder
- */
- public function date_range(string $date_range = ''): Builder
- {
- $parts = explode(",", $date_range);
-
- if (count($parts) != 2) {
- return $this->builder;
- }
-
- try {
-
- $start_date = Carbon::parse($parts[0]);
- $end_date = Carbon::parse($parts[1]);
-
- return $this->builder->whereBetween('date', [$start_date, $end_date]);
- } catch(\Exception $e) {
- return $this->builder;
- }
-
- }
-
- /**
- * Filter by due date range
- *
- * @param string $date_range
- * @return Builder
- */
- public function due_date_range(string $date_range = ''): Builder
- {
- $parts = explode(",", $date_range);
-
- if (count($parts) != 2) {
- return $this->builder;
- }
- try {
-
- $start_date = Carbon::parse($parts[0]);
- $end_date = Carbon::parse($parts[1]);
-
- return $this->builder->whereBetween('due_date', [$start_date, $end_date]);
- } catch(\Exception $e) {
- return $this->builder;
- }
-
- }
-
-
/**
* Sorts the list based on $sort.
*
diff --git a/app/Filters/QueryFilters.php b/app/Filters/QueryFilters.php
index 7f98a1d14e..c4ace3e0c6 100644
--- a/app/Filters/QueryFilters.php
+++ b/app/Filters/QueryFilters.php
@@ -331,4 +331,61 @@ abstract class QueryFilters
->orderByRaw("{$this->with_property} = ? DESC", [$value])
->company();
}
+
+
+ /**
+ * Filter by date range
+ *
+ * @param string $date_range
+ * @return Builder
+ */
+ public function date_range(string $date_range = ''): Builder
+ {
+ $parts = explode(",", $date_range);
+
+ if (count($parts) != 2 || !in_array('date', \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
+ return $this->builder;
+ }
+
+ try {
+
+ $start_date = Carbon::parse($parts[0]);
+ $end_date = Carbon::parse($parts[1]);
+
+ return $this->builder->whereBetween('date', [$start_date, $end_date]);
+ } catch(\Exception $e) {
+ return $this->builder;
+ }
+
+ }
+
+ /**
+ * Filter by due date range
+ *
+ * @param string $date_range
+ * @return Builder
+ */
+ public function due_date_range(string $date_range = ''): Builder
+ {
+
+ $parts = explode(",", $date_range);
+
+ if (count($parts) != 2 || !in_array('due_date', \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
+ return $this->builder;
+ }
+
+ try {
+
+ $start_date = Carbon::parse($parts[0]);
+ $end_date = Carbon::parse($parts[1]);
+
+ return $this->builder->whereBetween('due_date', [$start_date, $end_date]);
+ } catch(\Exception $e) {
+ return $this->builder;
+ }
+
+ }
+
+
+
}
diff --git a/app/Helpers/Invoice/ProRata.php b/app/Helpers/Invoice/ProRata.php
index c96166c64d..cae0b696d0 100644
--- a/app/Helpers/Invoice/ProRata.php
+++ b/app/Helpers/Invoice/ProRata.php
@@ -23,12 +23,12 @@ class ProRata
* the time interval and the frequency duration
*
* @param float $amount
- * @param Carbon $from_date
- * @param Carbon $to_date
+ * @param \Illuminate\Support\Carbon | \Carbon\Carbon $from_date
+ * @param \Illuminate\Support\Carbon | \Carbon\Carbon $to_date
* @param int $frequency
* @return float
*/
- public function refund(float $amount, Carbon $from_date, Carbon $to_date, int $frequency): float
+ public function refund(float $amount, $from_date, $to_date, int $frequency): float
{
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
$days_in_frequency = $this->getDaysInFrequency($frequency);
@@ -41,12 +41,12 @@ class ProRata
* the time interval and the frequency duration
*
* @param float $amount
- * @param Carbon $from_date
- * @param Carbon $to_date
+ * @param \Illuminate\Support\Carbon | \Carbon\Carbon $from_date
+ * @param \Illuminate\Support\Carbon | \Carbon\Carbon $to_date
* @param int $frequency
* @return float
*/
- public function charge(float $amount, Carbon $from_date, Carbon $to_date, int $frequency): float
+ public function charge(float $amount, $from_date, $to_date, int $frequency): float
{
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
$days_in_frequency = $this->getDaysInFrequency($frequency);
diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php
index 3262593f60..def4080a3c 100644
--- a/app/Http/Requests/Client/StoreClientRequest.php
+++ b/app/Http/Requests/Client/StoreClientRequest.php
@@ -13,13 +13,11 @@ namespace App\Http\Requests\Client;
use App\DataMapper\ClientSettings;
use App\Http\Requests\Request;
-use App\Http\ValidationRules\Client\CountryCodeExistsRule;
use App\Http\ValidationRules\Ninja\CanStoreClientsRule;
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
use App\Models\Client;
use App\Models\GroupSetting;
use App\Utils\Traits\MakesHash;
-use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\Rule;
class StoreClientRequest extends Request
diff --git a/app/Http/Requests/Company/StoreCompanyRequest.php b/app/Http/Requests/Company/StoreCompanyRequest.php
index 6db6cce58c..442eceec2c 100644
--- a/app/Http/Requests/Company/StoreCompanyRequest.php
+++ b/app/Http/Requests/Company/StoreCompanyRequest.php
@@ -50,7 +50,7 @@ class StoreCompanyRequest extends Request
$rules['portal_domain'] = 'sometimes|url';
} else {
if (Ninja::isHosted()) {
- $rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
+ $rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9-]{1,63}$/', new ValidSubdomain()];
} else {
$rules['subdomain'] = 'nullable|alpha_num';
}
diff --git a/app/Http/ValidationRules/Client/CountryCodeExistsRule.php b/app/Http/ValidationRules/Client/CountryCodeExistsRule.php
deleted file mode 100644
index 7b3a0b8abb..0000000000
--- a/app/Http/ValidationRules/Client/CountryCodeExistsRule.php
+++ /dev/null
@@ -1,59 +0,0 @@
-checkIfCodeExists($value); //if it exists, return false!
- }
-
- /**
- * @return string
- */
- public function message()
- {
- return 'Country code does not exist';
- }
-
- /**
- * @return bool
- */
- private function checkIfCodeExists($value): bool
- {
- $country = Country::where('iso_3166_2', $value)
- ->orWhere('iso_3166_3', $value)
- ->exists();
-
- if ($country) {
- return true;
- }
-
- return false;
- }
-}
diff --git a/app/Http/ValidationRules/Company/ValidCompanyQuantity.php b/app/Http/ValidationRules/Company/ValidCompanyQuantity.php
index ce472598fb..7f67d2b46b 100644
--- a/app/Http/ValidationRules/Company/ValidCompanyQuantity.php
+++ b/app/Http/ValidationRules/Company/ValidCompanyQuantity.php
@@ -12,36 +12,26 @@
namespace App\Http\ValidationRules\Company;
use App\Utils\Ninja;
-use Illuminate\Contracts\Validation\Rule;
+use Closure;
+use Illuminate\Contracts\Validation\ValidationRule;
/**
* Class ValidCompanyQuantity.
*/
-class ValidCompanyQuantity implements Rule
+class ValidCompanyQuantity implements ValidationRule
{
- /**
- * @param string $attribute
- * @param mixed $value
- * @return bool
- */
- public function passes($attribute, $value)
+
+ public function validate(string $attribute, mixed $value, Closure $fail): void
{
- if (config('ninja.testvars.travis')) {
- return true;
- }
+ $message = ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
- if (Ninja::isSelfHost()) {
- return auth()->user()->company()->account->companies->count() < 10;
- }
+ $test = Ninja::isSelfHost() ?
+ auth()->user()->company()->account->companies->count() < 10 :
+ (auth()->user()->account->isPaid() || auth()->user()->account->isTrial()) && auth()->user()->company()->account->companies->count() < 10 ;
- return (auth()->user()->account->isPaid() || auth()->user()->account->isTrial()) && auth()->user()->company()->account->companies->count() < 10 ;
+ if (!$test) {
+ $fail($message);
+ }
}
- /**
- * @return string
- */
- public function message()
- {
- return ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
- }
}
diff --git a/app/Http/ValidationRules/Company/ValidSubdomain.php b/app/Http/ValidationRules/Company/ValidSubdomain.php
index 48555e49c0..26aeb32349 100644
--- a/app/Http/ValidationRules/Company/ValidSubdomain.php
+++ b/app/Http/ValidationRules/Company/ValidSubdomain.php
@@ -12,31 +12,22 @@
namespace App\Http\ValidationRules\Company;
use App\Libraries\MultiDB;
-use Illuminate\Contracts\Validation\Rule;
+use Closure;
+use Illuminate\Contracts\Validation\ValidationRule;
/**
- * Class ValidCompanyQuantity.
+ * Class ValidSubdomain.
*/
-class ValidSubdomain implements Rule
+class ValidSubdomain implements ValidationRule
{
- public function __construct()
+ public function validate(string $attribute, mixed $value, Closure $fail): void
{
- }
- public function passes($attribute, $value)
- {
- if (empty($value)) {
- return true;
+ if(empty($value))
+ return;
+
+ if (!MultiDB::checkDomainAvailable($value)) {
+ $fail(ctrans('texts.subdomain_taken'));
}
-
- return MultiDB::checkDomainAvailable($value);
- }
-
- /**
- * @return string
- */
- public function message()
- {
- return ctrans('texts.subdomain_taken');
}
}
diff --git a/app/Livewire/Flow2/InvoicePay.php b/app/Livewire/Flow2/InvoicePay.php
index f67278d32b..2c2aecc86e 100644
--- a/app/Livewire/Flow2/InvoicePay.php
+++ b/app/Livewire/Flow2/InvoicePay.php
@@ -165,6 +165,7 @@ class InvoicePay extends Component
return $this->required_fields = true;
}
+ /** @var \App\Models\ClientContact $contact */
$contact = $this->getContext()['contact'];
foreach ($fields as $index => $field) {
@@ -173,7 +174,7 @@ class InvoicePay extends Component
if (\Illuminate\Support\Str::startsWith($field['name'], 'client_')) {
if (
empty($contact->client->{$_field})
- || is_null($contact->client->{$_field})
+ || is_null($contact->client->{$_field}) //@phpstan-ignore-line
) {
return $this->required_fields = true;
@@ -182,7 +183,7 @@ class InvoicePay extends Component
}
if (\Illuminate\Support\Str::startsWith($field['name'], 'contact_')) {
- if (empty($contact->{$_field}) || is_null($contact->{$_field}) || str_contains($contact->{$_field}, '@example.com')) {
+ if (empty($contact->{$_field}) || is_null($contact->{$_field}) || str_contains($contact->{$_field}, '@example.com')) { //@phpstan-ignore-line
return $this->required_fields = true;
}
}
diff --git a/app/Mail/Admin/ClientPaymentFailureObject.php b/app/Mail/Admin/ClientPaymentFailureObject.php
index 842d0f84fb..a667360930 100644
--- a/app/Mail/Admin/ClientPaymentFailureObject.php
+++ b/app/Mail/Admin/ClientPaymentFailureObject.php
@@ -123,7 +123,7 @@ class ClientPaymentFailureObject
$signature = $this->client->getSetting('email_signature');
$html_variables = (new HtmlEngine($invitation))->makeValues();
- $html_variables['$gateway_payment_error'] = $this->error ?? '';
+ $html_variables['$payment_error'] = $this->error ?? '';
$html_variables['$total'] = $this->getAmount();
$signature = str_replace(array_keys($html_variables), array_values($html_variables), $signature);
diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php
index 5ea237f04a..175940b1f6 100644
--- a/app/Models/BaseModel.php
+++ b/app/Models/BaseModel.php
@@ -298,12 +298,37 @@ class BaseModel extends Model
}
// special catch here for einvoicing eventing
- if($event_id == Webhook::EVENT_SENT_INVOICE && ($this instanceof Invoice) && is_null($this->backup)){
+ if($event_id == Webhook::EVENT_SENT_INVOICE && ($this instanceof Invoice) && is_null($this->backup) && $this->client->getSetting('e_invoice_type') == 'PEPPOL'){
\App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($this), $this->id, $this->company->db);
}
}
+
+ /**
+ * arrayFilterRecursive
+ *
+ * Removes null properties from an array
+ *
+ * @param array $array
+ * @return array
+ */
+ public function arrayFilterRecursive(array $array): array
+ {
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ // Recursively filter the nested array
+ $array[$key] = $this->arrayFilterRecursive($value);
+ }
+ // Remove null values
+ if (is_null($array[$key])) {
+ unset($array[$key]);
+ }
+ }
+
+ return $array;
+ }
+
/**
* Returns the base64 encoded PDF string of the entity
* @deprecated - unused implementation
diff --git a/app/Models/Client.php b/app/Models/Client.php
index c5ef8c2084..26c8ee79e6 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -50,7 +50,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property int|null $last_login
* @property int|null $industry_id
* @property int|null $size_id
- * @property object|null $e_invoice
+ * @property object|array|null $e_invoice
* @property string|null $address1
* @property string|null $address2
* @property string|null $city
diff --git a/app/Models/Project.php b/app/Models/Project.php
index c786dcec08..32a5ffabd2 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -37,6 +37,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property-read Project|null $project
* @property-read int|null $tasks_count
* @property-read \App\Models\User $user
+ * @property-read \App\Models\User $assigned_user
* @property-read \App\Models\Vendor|null $vendor
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
@@ -117,6 +118,11 @@ class Project extends BaseModel
return $this->belongsTo(User::class)->withTrashed();
}
+ public function assigned_user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
+ {
+ return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
+ }
+
public function tasks(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Task::class);
diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php
index 4ee0e8dae9..3dca62962f 100644
--- a/app/PaymentDrivers/BaseDriver.php
+++ b/app/PaymentDrivers/BaseDriver.php
@@ -400,8 +400,12 @@ class BaseDriver extends AbstractPaymentDriver
$invoice = $this->payment_hash->fee_invoice;
$fee_count = collect($invoice->line_items)
+ ->map(function ($item){
+ $item->gross_line_total = round($item->gross_line_total, 2);
+ return $item;
+ })
->whereIn('type_id', ['3','4'])
- ->where('gross_line_total', $fee_total)
+ ->where('gross_line_total', round($fee_total,2))
->count();
if($invoice && $fee_count == 0){
@@ -425,15 +429,15 @@ class BaseDriver extends AbstractPaymentDriver
$invoice_items = (array) $invoice->line_items;
$invoice_items[] = $invoice_item;
- if (isset($data['gateway_type_id']) && $fees_and_limits = $this->company_gateway->getFeesAndLimits($data['gateway_type_id'])) {
- $invoice_item->tax_rate1 = $fees_and_limits->fee_tax_rate1;
- $invoice_item->tax_name1 = $fees_and_limits->fee_tax_name1;
- $invoice_item->tax_rate2 = $fees_and_limits->fee_tax_rate2;
- $invoice_item->tax_name2 = $fees_and_limits->fee_tax_name2;
- $invoice_item->tax_rate3 = $fees_and_limits->fee_tax_rate3;
- $invoice_item->tax_name3 = $fees_and_limits->fee_tax_name3;
- $invoice_item->tax_id = (string)\App\Models\Product::PRODUCT_TYPE_OVERRIDE_TAX;
- }
+ if (isset($data['gateway_type_id']) && $fees_and_limits = $this->company_gateway->getFeesAndLimits($data['gateway_type_id'])) {
+ $invoice_item->tax_rate1 = $fees_and_limits->fee_tax_rate1;
+ $invoice_item->tax_name1 = $fees_and_limits->fee_tax_name1;
+ $invoice_item->tax_rate2 = $fees_and_limits->fee_tax_rate2;
+ $invoice_item->tax_name2 = $fees_and_limits->fee_tax_name2;
+ $invoice_item->tax_rate3 = $fees_and_limits->fee_tax_rate3;
+ $invoice_item->tax_name3 = $fees_and_limits->fee_tax_name3;
+ $invoice_item->tax_id = (string)\App\Models\Product::PRODUCT_TYPE_OVERRIDE_TAX;
+ }
$invoice->line_items = $invoice_items;
diff --git a/app/PaymentDrivers/GoCardless/InstantBankPay.php b/app/PaymentDrivers/GoCardless/InstantBankPay.php
index 9133a47a68..624f04eebe 100644
--- a/app/PaymentDrivers/GoCardless/InstantBankPay.php
+++ b/app/PaymentDrivers/GoCardless/InstantBankPay.php
@@ -105,6 +105,9 @@ class InstantBankPay implements MethodInterface
$this->go_cardless->payment_hash->data->billing_request
);
+ nlog($billing_request);
+
+
$payment = $this->go_cardless->gateway->payments()->get(
$billing_request->payment_request->links->payment
);
diff --git a/app/PaymentDrivers/MolliePaymentDriver.php b/app/PaymentDrivers/MolliePaymentDriver.php
index d35e8d158e..8f98a2faeb 100644
--- a/app/PaymentDrivers/MolliePaymentDriver.php
+++ b/app/PaymentDrivers/MolliePaymentDriver.php
@@ -286,7 +286,8 @@ class MolliePaymentDriver extends BaseDriver
public function processWebhookRequest(PaymentWebhookRequest $request)
{
// Allow app to catch up with webhook request.
- sleep(4);
+ // sleep(4);
+ usleep(rand(2800000, 4000000));
$validator = Validator::make($request->all(), [
'id' => ['required', 'starts_with:tr'],
diff --git a/app/PaymentDrivers/Stripe/ImportCustomers.php b/app/PaymentDrivers/Stripe/ImportCustomers.php
index 7eef0391e6..c58193f104 100644
--- a/app/PaymentDrivers/Stripe/ImportCustomers.php
+++ b/app/PaymentDrivers/Stripe/ImportCustomers.php
@@ -125,6 +125,13 @@ class ImportCustomers
$settings->currency_id = (string) $currency->id;
$client->settings = $settings;
}
+
+ }else {
+
+ $settings = $client->settings;
+ $settings->currency_id = (string) $this->stripe->company_gateway->company->settings->currency_id;
+ $client->settings = $settings;
+
}
$client->name = $customer->name ? $customer->name : $customer->email;
diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php
index 3e4ff6b4e5..54b43d130e 100644
--- a/app/Repositories/BaseRepository.php
+++ b/app/Repositories/BaseRepository.php
@@ -147,8 +147,7 @@ class BaseRepository
* @throws \ReflectionException
*/
protected function alternativeSave($data, $model)
- { //$start = microtime(true);
- //forces the client_id if it doesn't exist
+ {
if (array_key_exists('client_id', $data)) {
$model->client_id = $data['client_id'];
}
@@ -167,7 +166,7 @@ class BaseRepository
$company_defaults = $client->setCompanyDefaults($data, lcfirst($resource));
$data['exchange_rate'] = $company_defaults['exchange_rate'];
$model->uses_inclusive_taxes = $client->getSetting('inclusive_taxes');
- // $data = array_merge($company_defaults, $data);
+
$data = array_merge($data, $company_defaults);
}
@@ -321,6 +320,17 @@ class BaseRepository
UpdateTaxData::dispatch($client, $client->company);
}
+ /** If Peppol is enabled - we will save the e_invoice document here at this point, document will not update after being sent */
+ if(strlen($model->backup ?? '') == 0 && $client->getSetting('e_invoice_type') == 'PEPPOL' && $model->company->legal_entity_id)
+ {
+ try{
+ $model->service()->getEInvoice();
+ }
+ catch(\Exception $e){
+ nlog("EXCEPTION:: BASEREPOSITORY:: Error generating e_invoice for model {$model->id}");
+ nlog($e->getMessage());
+ }
+ }
}
if ($model instanceof Credit) {
diff --git a/app/Repositories/ClientRepository.php b/app/Repositories/ClientRepository.php
index 8c52b83f95..dc77bb4827 100644
--- a/app/Repositories/ClientRepository.php
+++ b/app/Repositories/ClientRepository.php
@@ -76,6 +76,13 @@ class ClientRepository extends BaseRepository
$client->country_id = $company->settings->country_id;
}
+ if(isset($data['e_invoice']) && is_array($data['e_invoice'])) {
+ //ensure it is normalized first!
+ $data['e_invoice'] = $client->arrayFilterRecursive($data['e_invoice']);
+
+ $client->e_invoice = $data['e_invoice'];
+ }
+
$client->save();
if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) {
@@ -106,6 +113,7 @@ class ClientRepository extends BaseRepository
if (array_key_exists('contacts', $contact_data) || $client->contacts()->count() == 0) {
$this->contact_repo->save($contact_data, $client);
}
+
return $client;
}
diff --git a/app/Repositories/CompanyRepository.php b/app/Repositories/CompanyRepository.php
index a2a2195d3b..44bf6b2cbe 100644
--- a/app/Repositories/CompanyRepository.php
+++ b/app/Repositories/CompanyRepository.php
@@ -60,7 +60,7 @@ class CompanyRepository extends BaseRepository
if(isset($data['e_invoice']) && is_array($data['e_invoice'])){
//ensure it is normalized first!
- $data['e_invoice'] = $this->arrayFilterRecursive($data['e_invoice']);
+ $data['e_invoice'] = $company->arrayFilterRecursive($data['e_invoice']);
$company->e_invoice = $data['e_invoice'];
}
@@ -70,24 +70,6 @@ class CompanyRepository extends BaseRepository
return $company;
}
-
- private function arrayFilterRecursive(array $array): array
- {
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- // Recursively filter the nested array
- $array[$key] = $this->arrayFilterRecursive($value);
- }
- // Remove null values
- if (is_null($array[$key])) {
- unset($array[$key]);
- }
- }
-
- return $array;
- }
-
-
/**
* parseCustomFields
*
diff --git a/app/Services/Client/RFFService.php b/app/Services/Client/RFFService.php
index a3983d7c95..08b4b3b467 100644
--- a/app/Services/Client/RFFService.php
+++ b/app/Services/Client/RFFService.php
@@ -69,7 +69,7 @@ class RFFService
if (Str::startsWith($field['name'], 'client_')) {
if (
empty($_contact->client->{$_field})
- || is_null($_contact->client->{$_field})
+ || is_null($_contact->client->{$_field}) //@phpstan-ignore-line
) {
// $this->show_form = true;
$this->unfilled_fields++;
@@ -79,7 +79,7 @@ class RFFService
}
if (Str::startsWith($field['name'], 'contact_')) {
- if (empty($_contact->{$_field}) || is_null($_contact->{$_field}) || str_contains($_contact->{$_field}, '@example.com')) {
+ if (empty($_contact->{$_field}) || is_null($_contact->{$_field}) || str_contains($_contact->{$_field}, '@example.com')) { //@phpstan-ignore-line
$this->unfilled_fields++;
} else {
$this->fields[$index]['filled'] = true;
diff --git a/app/Services/EDocument/Gateway/Storecove/Storecove.php b/app/Services/EDocument/Gateway/Storecove/Storecove.php
index 335012f473..d362eea22f 100644
--- a/app/Services/EDocument/Gateway/Storecove/Storecove.php
+++ b/app/Services/EDocument/Gateway/Storecove/Storecove.php
@@ -261,7 +261,7 @@ class Storecove
*
* @param int $id
* @param array $data
- * @return array
+ * @return mixed
*/
public function updateLegalEntity(int $id, array $data)
{
diff --git a/app/Services/EDocument/Jobs/SendEDocument.php b/app/Services/EDocument/Jobs/SendEDocument.php
index a1d6e1d429..7528fc227a 100644
--- a/app/Services/EDocument/Jobs/SendEDocument.php
+++ b/app/Services/EDocument/Jobs/SendEDocument.php
@@ -50,10 +50,11 @@ class SendEDocument implements ShouldQueue
MultiDB::setDB($this->db);
$model = $this->entity::find($this->id);
- $e_invoice_standard = $model->client ? $model->client->getSetting('e_invoice_type') : $model->company->getSetting('e_invoice_type');
- if($e_invoice_standard != 'PEPPOL')
- return;
+ // $e_invoice_standard = $model->client ? $model->client->getSetting('e_invoice_type') : $model->company->getSetting('e_invoice_type');
+
+ // if($e_invoice_standard != 'PEPPOL')
+ // return;
if(Ninja::isSelfHost() && ($model instanceof Invoice) && $model->company->legal_entity_id)
{
@@ -122,10 +123,11 @@ class SendEDocument implements ShouldQueue
$activity->company_id = $model->company_id;
$activity->activity_type_id = Activity::EMAIL_EINVOICE_SUCCESS;
$activity->invoice_id = $model->id;
- $activity->notes = $guid;
+ $activity->notes = str_replace('"', '', $guid);
+
$activity->save();
- $model->backup = $guid;
+ $model->backup = str_replace('"', '', $guid);
$model->saveQuietly();
}
diff --git a/app/Services/EDocument/Standards/OrderXDocument.php b/app/Services/EDocument/Standards/OrderXDocument.php
index de3af56868..32200b9a3d 100644
--- a/app/Services/EDocument/Standards/OrderXDocument.php
+++ b/app/Services/EDocument/Standards/OrderXDocument.php
@@ -100,7 +100,7 @@ class OrderXDocument extends AbstractService
$this->orderxdocument->setDocumentShipToAddress($settings_entity->shipping_address1, $settings_entity->shipping_address2, "", $settings_entity->shipping_postal_code, $settings_entity->shipping_city, $settings_entity->shipping_country->iso_3166_2, $settings_entity->shipping_state);
}
- $this->orderxdocument->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment"));
+ $this->orderxdocument->addDocumentPaymentMean('68', ctrans("texts.xinvoice_online_payment"));
if (str_contains($company->getSetting('vat_number'), "/")) {
$this->orderxdocument->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number'));
diff --git a/app/Services/EDocument/Standards/Peppol.php b/app/Services/EDocument/Standards/Peppol.php
index 11adde48f8..bce608b94c 100644
--- a/app/Services/EDocument/Standards/Peppol.php
+++ b/app/Services/EDocument/Standards/Peppol.php
@@ -276,7 +276,7 @@ class Peppol extends AbstractService
$this->p_invoice->DueDate = new \DateTime($this->invoice->due_date);
}
- $this->p_invoice->InvoiceTypeCode = 380; //
+ $this->p_invoice->InvoiceTypeCode = ($this->invoice->amount >= 0) ? 380 : 381; //
$this->p_invoice->AccountingSupplierParty = $this->getAccountingSupplierParty();
$this->p_invoice->AccountingCustomerParty = $this->getAccountingCustomerParty();
$this->p_invoice->InvoiceLine = $this->getInvoiceLines();
@@ -287,8 +287,11 @@ class Peppol extends AbstractService
$this->senderSpecificLevelMutators()
->receiverSpecificLevelMutators();
- $this->invoice->e_invoice = $this->toObject();
- $this->invoice->save();
+ if(strlen($this->invoice->backup ?? '') == 0)
+ {
+ $this->invoice->e_invoice = $this->toObject();
+ $this->invoice->save();
+ }
return $this;
@@ -335,7 +338,7 @@ class Peppol extends AbstractService
/**
* getInvoice
*
- * @return InvoiceNinja\EInvoice\Models\Peppol\Invoice
+ * @return \InvoiceNinja\EInvoice\Models\Peppol\Invoice
*/
public function getInvoice(): \InvoiceNinja\EInvoice\Models\Peppol\Invoice
{
diff --git a/app/Services/EDocument/Standards/ZugferdEDokument.php b/app/Services/EDocument/Standards/ZugferdEDokument.php
index 4e261fd1b7..74555942c3 100644
--- a/app/Services/EDocument/Standards/ZugferdEDokument.php
+++ b/app/Services/EDocument/Standards/ZugferdEDokument.php
@@ -124,9 +124,9 @@ class ZugferdEDokument extends AbstractService
//Payment Means - Switcher
if($company->settings->custom_value1 == '42') {
- $this->xdocument->addDocumentPaymentMean(typecode: 42, payeeIban: $company->settings->custom_value2, payeeAccountName: $company->settings->custom_value4, payeeBic: $company->settings->custom_value3);
+ $this->xdocument->addDocumentPaymentMean(typecode: '42', payeeIban: $company->settings->custom_value2, payeeAccountName: $company->settings->custom_value4, payeeBic: $company->settings->custom_value3);
} else {
- $this->xdocument->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment"));
+ $this->xdocument->addDocumentPaymentMean('68', ctrans("texts.xinvoice_online_payment"));
}
if (str_contains($company->getSetting('vat_number'), "/")) {
diff --git a/app/Services/Invoice/AddGatewayFee.php b/app/Services/Invoice/AddGatewayFee.php
index fc1365e923..386b8a8e05 100644
--- a/app/Services/Invoice/AddGatewayFee.php
+++ b/app/Services/Invoice/AddGatewayFee.php
@@ -92,6 +92,8 @@ class AddGatewayFee extends AbstractService
/**Refresh Invoice values*/
$this->invoice = $this->invoice->calc()->getInvoice();
+ nlog($this->invoice->line_items);
+
$new_balance = $this->invoice->balance;
if (floatval($new_balance) - floatval($balance) != 0) {
@@ -141,12 +143,6 @@ class AddGatewayFee extends AbstractService
if (floatval($new_balance) - floatval($balance) != 0) {
$adjustment = $new_balance - $balance;
- // $this->invoice
- // ->client
- // ->service()
- // ->updateBalance($adjustment * -1)
- // ->save();
-
$this->invoice
->ledger()
->updateInvoiceBalance($adjustment * -1, 'Adjustment for adding gateway DISCOUNT');
diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php
index cfb686dac5..3c7fc0bb9c 100644
--- a/app/Services/Invoice/AutoBillInvoice.php
+++ b/app/Services/Invoice/AutoBillInvoice.php
@@ -148,9 +148,9 @@ class AutoBillInvoice extends AbstractService
]);
nlog("Payment hash created => {$payment_hash->id}");
+ $this->invoice->saveQuietly();
$payment = false;
-
try {
$payment = $gateway_token->gateway
->driver($this->client)
@@ -163,6 +163,7 @@ class AutoBillInvoice extends AbstractService
}
+ $this->invoice = $this->invoice->fresh();
$this->invoice->auto_bill_tries += 1;
if ($this->invoice->auto_bill_tries == 3) {
diff --git a/app/Services/Quickbooks/Jobs/QuickbooksSync.php b/app/Services/Quickbooks/Jobs/QuickbooksSync.php
index 86057a06bd..0b82e5a357 100644
--- a/app/Services/Quickbooks/Jobs/QuickbooksSync.php
+++ b/app/Services/Quickbooks/Jobs/QuickbooksSync.php
@@ -102,9 +102,9 @@ class QuickbooksSync implements ShouldQueue
private function processEntitySync(string $entity, $records)
{
match($entity){
- // 'client' => $this->syncQbToNinjaClients($records),
+ 'client' => $this->syncQbToNinjaClients($records),
'product' => $this->syncQbToNinjaProducts($records),
- // 'invoice' => $this->syncQbToNinjaInvoices($records),
+ 'invoice' => $this->syncQbToNinjaInvoices($records),
// 'vendor' => $this->syncQbToNinjaClients($records),
// 'quote' => $this->syncInvoices($records),
// 'purchase_order' => $this->syncInvoices($records),
diff --git a/app/Services/Template/TemplateService.php b/app/Services/Template/TemplateService.php
index c4894b674d..4810c42dfa 100644
--- a/app/Services/Template/TemplateService.php
+++ b/app/Services/Template/TemplateService.php
@@ -949,6 +949,7 @@ class TemplateService
'custom_value4' => $task->custom_value4 ?: '',
'status' => $task->status ? $task->status->name : '',
'user' => $this->userInfo($task->user),
+ 'assigned_user' => $task->assigned_user ? $this->userInfo($task->assigned_user) : [],
'client' => $this->getClient($task),
];
@@ -1007,6 +1008,7 @@ class TemplateService
'tasks' => ($project->tasks && !$nested) ? $this->processTasks($project->tasks, true) : [], //@phpstan-ignore-line
'client' => $this->getClient($project),
'user' => $this->userInfo($project->user),
+ 'assigned_user' => $project->assigned_user ? $this->userInfo($project->assigned_user) : [],
'invoices' => $this->processInvoices($project->invoices)
];
diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php
index e39f257e06..b2cf9f9176 100644
--- a/app/Utils/HtmlEngine.php
+++ b/app/Utils/HtmlEngine.php
@@ -401,11 +401,17 @@ class HtmlEngine
$data['$user.name'] = ['value' => $this->entity->user->present()->name(), 'label' => ctrans('texts.name')];
+
$data['$user.signature'] = ['value' => $this->entity->user->signature ?? '', 'label' => ctrans('texts.signature')];
$data['$user.first_name'] = ['value' => $this->entity->user->first_name, 'label' => ctrans('texts.first_name')];
$data['$user.last_name'] = ['value' => $this->entity->user->last_name, 'label' => ctrans('texts.last_name')];
$data['$created_by_user'] = &$data['$user.name'];
+
$data['$assigned_to_user'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->present()->name() : '', 'label' => ctrans('texts.name')];
+ $data['$assigned_user.signature'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->signature : '', 'label' => ctrans('texts.signature')];
+
+ $data['$assigned_user.first_name'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->first_name : '', 'label' => ctrans('texts.first_name')];
+ $data['$assigned_user.last_name'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->last_name : '', 'label' => ctrans('texts.last_name')];
$data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')];
@@ -666,7 +672,7 @@ class HtmlEngine
}
$data['$contact.signature_raw'] = ['value' => $this->invitation->signature_base64, 'label' => ctrans('texts.signature')];
- $data['$contact.signature_date'] = ['value' => $this->translateDate($this->invitation->signature_date ?? '1970-01-01', $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.date')];
+ $data['$contact.signature_date'] = ['value' => $this->invitation->signature_date ? $this->translateDate($this->invitation->signature_date, $this->client->date_format(), $this->client->locale()) : ' ', 'label' => ctrans('texts.date')];
$data['$contact.signature_ip'] = ['value' => $this->invitation->signature_ip ?? '', 'label' => ctrans('texts.address')];
$data['$thanks'] = ['value' => '', 'label' => ctrans('texts.thanks')];
@@ -733,7 +739,7 @@ class HtmlEngine
$data['$payment.number'] = ['value' => '', 'label' => ctrans('texts.payment_number')];
$data['$payment.transaction_reference'] = ['value' => '', 'label' => ctrans('texts.transaction_reference')];
$data['$payment.refunded'] = ['value' => '', 'label' => ctrans('texts.refund')];
- $data['$gateway_payment_error'] = ['value' => '', 'label' => ctrans('texts.error')];
+ $data['$payment_error'] = ['value' => '', 'label' => ctrans('texts.error')];
if ($this->entity_string == 'invoice' && $this->entity->net_payments()->exists()) {
$payment_list = '
';
@@ -1166,7 +1172,7 @@ class HtmlEngine
}
return '
-