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 ' -
+