diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 9edd42f4dfba..94a1c6e105ec 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -890,6 +890,40 @@ class CheckData extends Command $this->logMessage("Fixing country for # {$client->id}"); }); + + Client::query()->whereNull("settings->currency_id")->cursor()->each(function ($client){ + $settings = $client->settings; + $settings->currency_id = (string)$client->company->settings->currency_id; + $client->settings = $settings; + $client->saveQuietly(); + + $this->logMessage("Fixing currency_id for # {$client->id}"); + + }); + + Payment::withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment){ + $payment->exchange_rate = 1; + $payment->saveQuietly(); + + $this->logMessage("Fixing exchange rate for # {$payment->id}"); + }); + + Payment::withTrashed() + ->whereHas("client", function ($query) { + $query->whereColumn("settings->currency_id", "!=", "payments.currency_id"); + }) + ->cursor() + ->each(function ($p) { + $p->currency_id = $p->client->settings->currency_id; + $p->saveQuietly(); + + + $this->logMessage("Fixing currency for # {$p->id}"); + + }); + + + } } diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 41f9abd86799..ccc0d46a4449 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -319,6 +319,7 @@ class BaseRule implements RuleInterface Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt($item), Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced($item), Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override($item), + Product::PRODUCT_TYPE_ZERO_RATED => $this->zeroRated($item), default => $this->defaultForeign(), }; @@ -327,6 +328,14 @@ class BaseRule implements RuleInterface } + public function zeroRated($item): self + { + $this->tax_rate1 = 0; + $this->tax_name1 = ctrans('texts.zero_rated'); + + return $this; + } + public function taxByType(mixed $type): self { return $this; diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index d1a6a9e12458..1c251a6713a4 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -11,6 +11,7 @@ namespace App\DataMapper\Tax\DE; +use App\DataMapper\InvoiceItem; use App\DataMapper\Tax\BaseRule; use App\DataMapper\Tax\RuleInterface; use App\Models\Product; @@ -63,7 +64,7 @@ class Rule extends BaseRule implements RuleInterface public function taxByType($item): self { - if ($this->client->is_tax_exempt || !property_exists($item, 'tax_id')) { + if ($this->client->is_tax_exempt || !property_exists($item, 'tax_id') || (isset($item->type_id) && $item->type_id == '5')) { return $this->taxExempt($item); } diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index ce4a08280afd..b2af3a1b560e 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -399,8 +399,7 @@ class QuoteController extends BaseController $quote->service() ->triggeredActions($request); - // ->deletePdf(); - + event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); return $this->itemResponse($quote); diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php index 49f4f0b28bd5..b51264af116e 100644 --- a/app/Jobs/Mail/NinjaMailerJob.php +++ b/app/Jobs/Mail/NinjaMailerJob.php @@ -51,10 +51,6 @@ class NinjaMailerJob implements ShouldQueue public $deleteWhenMissingModels = true; - public $nmo; - - public $override; - /** @var null|\App\Models\Company $company **/ public ?Company $company; @@ -67,11 +63,8 @@ class NinjaMailerJob implements ShouldQueue protected $client_mailgun_domain = false; - public function __construct(NinjaMailerObject $nmo, bool $override = false) + public function __construct(public ?NinjaMailerObject $nmo, public bool $override = false) { - $this->nmo = $nmo; - - $this->override = $override; } public function backoff() @@ -106,7 +99,10 @@ class NinjaMailerJob implements ShouldQueue } $this->nmo->mailable->replyTo($this->nmo->settings->reply_to_email, $reply_to_name); - } else { + }elseif(isset($this->nmo->invitation->user)){ + $this->nmo->mailable->replyTo($this->nmo->invitation->user->email, $this->nmo->invitation->user->present()->name()); + } + else { $this->nmo->mailable->replyTo($this->company->owner()->email, $this->company->owner()->present()->name()); } diff --git a/app/Jobs/Mail/NinjaMailerObject.php b/app/Jobs/Mail/NinjaMailerObject.php index df33c35c2a11..f2cf0f646111 100644 --- a/app/Jobs/Mail/NinjaMailerObject.php +++ b/app/Jobs/Mail/NinjaMailerObject.php @@ -16,6 +16,8 @@ namespace App\Jobs\Mail; */ class NinjaMailerObject { + + /* @var Illuminate\Mail\Mailable */ public $mailable; /* @var Company $company */ @@ -32,7 +34,7 @@ class NinjaMailerObject /* Variable for cascading notifications */ public $entity_string = false; - /* @var bool | App\Models\InvoiceInvitation | App\Models\QuoteInvitation | App\Models\CreditInvitation | App\Models\RecurringInvoiceInvitation | App\Models\PurchaseOrderInvitation $invitation*/ + /* @var App\Models\InvoiceInvitation | App\Models\QuoteInvitation | App\Models\CreditInvitation | App\Models\RecurringInvoiceInvitation | App\Models\PurchaseOrderInvitation | \bool $invitation*/ public $invitation = false; public $template = false; diff --git a/app/Jobs/Product/UpdateOrCreateProduct.php b/app/Jobs/Product/UpdateOrCreateProduct.php index 553944715b3a..702e8c33c0f4 100644 --- a/app/Jobs/Product/UpdateOrCreateProduct.php +++ b/app/Jobs/Product/UpdateOrCreateProduct.php @@ -69,8 +69,11 @@ class UpdateOrCreateProduct implements ShouldQueue * we do NOT update the product details this short block we * check for the presence of a task_id and/or expense_id */ - $expense_count = count(array_column((array) $this->products, 'expense_id')); - $task_count = count(array_column((array) $this->products, 'task_id')); + // $expense_count = count(array_column((array) $this->products, 'expense_id')); + // $task_count = count(array_column((array) $this->products, 'task_id')); + + $task_count = implode("", array_column((array) $this->products, 'task_id')); + $expense_count = implode("", array_column((array) $this->products, 'expense_id')); if ($task_count >= 1 || $expense_count >= 1) { return; diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 07a59c8b97cd..3cf8a071ac23 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -44,7 +44,7 @@ use Laracasts\Presenter\PresentableTrait; * @property string|null $po_number * @property string|null $date * @property string|null $last_sent_date - * @property string|null $due_date + * @property string|null|Carbon $due_date * @property string|null $next_send_date * @property bool $is_deleted * @property object|null $line_items @@ -164,7 +164,7 @@ class Quote extends BaseModel protected $casts = [ // 'date' => 'date:Y-m-d', - // 'due_date' => 'date:Y-m-d', + 'due_date' => 'date:Y-m-d', // 'partial_due_date' => 'date:Y-m-d', 'line_items' => 'object', 'backup' => 'object', @@ -197,7 +197,7 @@ class Quote extends BaseModel public function getDueDateAttribute($value) { - return $this->dateMutator($value); + return $value ? $this->dateMutator($value) : null; } public function getPartialDueDateAttribute($value) diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php index 510a66b10db7..fd9a5c1cf2f7 100644 --- a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php +++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php @@ -437,7 +437,7 @@ class PayPalPPCPPaymentDriver extends BaseDriver if($shipping = $this->getShippingAddress()) { - $order['purchase_units'][0] = $shipping; + $order['purchase_units'][0]["shipping"] = $shipping; } $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); @@ -465,8 +465,7 @@ class PayPalPPCPPaymentDriver extends BaseDriver { return $this->company_gateway->require_shipping_address ? [ - "shipping" => [ - "address" => + "address" => [ "address_line_1" => $this->client->shipping_address1, "address_line_2" => $this->client->shipping_address2, @@ -475,8 +474,8 @@ class PayPalPPCPPaymentDriver extends BaseDriver "postal_code" => $this->client->shipping_postal_code, "country_code" => $this->client->present()->shipping_country_code(), ], - ] ] + : null; } diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index c01d5380941c..6598bac40d9f 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -208,9 +208,22 @@ class EmailDefaults */ private function setReplyTo(): self { - $reply_to_email = str_contains($this->email->email_object->settings->reply_to_email, "@") ? $this->email->email_object->settings->reply_to_email : $this->email->company->owner()->email; + $reply_to_email = $this->email->company->owner()->email; + $reply_to_name = $this->email->company->owner()->present()->name(); - $reply_to_name = strlen($this->email->email_object->settings->reply_to_name) > 3 ? $this->email->email_object->settings->reply_to_name : $this->email->company->owner()->present()->name(); + if(str_contains($this->email->email_object->settings->reply_to_email, "@")){ + $reply_to_email = $this->email->email_object->settings->reply_to_email; + } + elseif(isset($this->email->email_object->invitation->user)) { + $reply_to_email = $this->email->email_object->invitation->user->email; + } + + if(strlen($this->email->email_object->settings->reply_to_name) > 3) { + $reply_to_name =$this->email->email_object->settings->reply_to_name; + } + elseif(isset($this->email->email_object->invitation->user)) { + $reply_to_name = $this->email->email_object->invitation->user->present()->name(); + } $this->email->email_object->reply_to = array_merge($this->email->email_object->reply_to, [new Address($reply_to_email, $reply_to_name)]); diff --git a/app/Services/Quote/MarkSent.php b/app/Services/Quote/MarkSent.php index 7c9c441ed4ec..f8d1c76d6ba7 100644 --- a/app/Services/Quote/MarkSent.php +++ b/app/Services/Quote/MarkSent.php @@ -42,7 +42,6 @@ class MarkSent ->service() ->setStatus(Quote::STATUS_SENT) ->applyNumber() - // ->deletePdf() ->save(); event(new QuoteWasMarkedSent($this->quote, $this->quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); diff --git a/database/migrations/2024_01_09_084515_product_cost_field_population.php b/database/migrations/2024_01_09_084515_product_cost_field_population.php index fd3ab9a43258..c38dd91d256a 100644 --- a/database/migrations/2024_01_09_084515_product_cost_field_population.php +++ b/database/migrations/2024_01_09_084515_product_cost_field_population.php @@ -28,20 +28,22 @@ return new class extends Migration $line_items = $invoice->line_items; - foreach ($line_items as $key => $item) + if(is_array($line_items)) { - - if($product = Product::where('company_id', $invoice->company_id)->where('product_key', $item->product_key)->where('cost', '>', 0)->first()) + foreach ($line_items as $key => $item) { - if((property_exists($item, 'product_cost') && $item->product_cost == 0) || !property_exists($item, 'product_cost')) - $line_items[$key]->product_cost = (float)$product->cost; + + if($product = Product::where('company_id', $invoice->company_id)->where('product_key', $item->product_key)->where('cost', '>', 0)->first()) + { + if((property_exists($item, 'product_cost') && $item->product_cost == 0) || !property_exists($item, 'product_cost')) + $line_items[$key]->product_cost = (float)$product->cost; + } + } - + + $invoice->line_items = $line_items; + $invoice->saveQuietly(); } - - $invoice->line_items = $line_items; - $invoice->saveQuietly(); - }); }