Merge branch 'v5-develop' into v5-stable
This commit is contained in:
commit
69a9225957
|
|
@ -16,10 +16,11 @@ jobs:
|
|||
extensions: mysql, mysqlnd, sqlite3, bcmath, gd, curl, zip, openssl, mbstring, xml
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: v5-develop
|
||||
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Copy .env file
|
||||
run: |
|
||||
cp .env.example .env
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
5.10.53
|
||||
5.10.62
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use App\DataMapper\EInvoice\TaxEntity;
|
||||
use App\DataMapper\Referral\ReferralEarning;
|
||||
|
||||
class AsReferralEarningCollection implements CastsAttributes
|
||||
{
|
||||
public function get($model, string $key, $value, array $attributes)
|
||||
{
|
||||
if (!$value || (is_string($value) && $value == "null")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = json_decode($value, true);
|
||||
|
||||
return array_map(fn ($item) => new ReferralEarning($item), $items);
|
||||
}
|
||||
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
if (!$value) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
if ($value instanceof ReferralEarning) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
return json_encode(array_map(fn ($entity) => get_object_vars($entity), $value));
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,8 @@ class SendRemindersCron extends Command
|
|||
});
|
||||
|
||||
if ($invoice->invitations->count() > 0) {
|
||||
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
|
||||
// event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
|
||||
$invoice->entityEmailEvent($invoice->invitations->first(), $reminder_template);
|
||||
}
|
||||
}
|
||||
$invoice->service()->setReminder()->save();
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class Kernel extends ConsoleKernel
|
|||
$schedule->job(new UpdateExchangeRates())->dailyAt('23:30')->withoutOverlapping()->name('exchange-rate-job')->onOneServer();
|
||||
|
||||
/* Runs cleanup code for subscriptions */
|
||||
$schedule->job(new SubscriptionCron())->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer();
|
||||
$schedule->job(new SubscriptionCron())->hourlyAt(1)->withoutOverlapping()->name('subscription-job')->onOneServer();
|
||||
|
||||
/* Sends recurring expenses*/
|
||||
$schedule->job(new RecurringExpensesCron())->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Referral;
|
||||
|
||||
class ReferralEarning
|
||||
{
|
||||
/** @var string $version */
|
||||
public string $version = 'alpha';
|
||||
|
||||
public string $referral_start_date = ''; // The date this referral was registered.
|
||||
|
||||
public string $qualifies_after = ''; // The date the payout qualifies after (5 months / 1 year)
|
||||
|
||||
public string $period_ending = ''; // The Date this set relates to. ie 2024-07-31 = July 2024
|
||||
|
||||
public string $account_key = '';
|
||||
|
||||
public string $payout_status = 'pending'; //pending //qualified //paidout //invalid
|
||||
|
||||
public float $gross_amount = 0;
|
||||
|
||||
public float $commission_amount = 0;
|
||||
|
||||
public string $notes = '';
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param mixed $entity
|
||||
*/
|
||||
public function __construct(mixed $entity = null)
|
||||
{
|
||||
if (!$entity) {
|
||||
$this->init();
|
||||
return;
|
||||
}
|
||||
|
||||
$entityArray = is_object($entity) ? get_object_vars($entity) : $entity;
|
||||
|
||||
foreach ($entityArray as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
|
||||
$this->migrate();
|
||||
}
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function migrate(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -111,7 +111,35 @@ class BaseRule implements RuleInterface
|
|||
];
|
||||
|
||||
/** EU TAXES */
|
||||
|
||||
/** Supported E Delivery Countries */
|
||||
public array $peppol_business_countries = [
|
||||
'AT',
|
||||
'BE',
|
||||
'DK',
|
||||
'EE',
|
||||
'FI',
|
||||
'DE',
|
||||
'IS',
|
||||
'IT',
|
||||
'LT',
|
||||
'LU',
|
||||
'NL',
|
||||
'NO',
|
||||
'PL',
|
||||
'SE',
|
||||
];
|
||||
|
||||
public array $peppol_government_countries = [
|
||||
'FR',
|
||||
'GR',
|
||||
'PT',
|
||||
'RO',
|
||||
'SI',
|
||||
'ES',
|
||||
'GB',
|
||||
];
|
||||
/** Supported E Delivery Countries */
|
||||
|
||||
public string $tax_name1 = '';
|
||||
public float $tax_rate1 = 0;
|
||||
|
|
|
|||
|
|
@ -95,31 +95,35 @@ class TaxModel
|
|||
}
|
||||
|
||||
//@pending Flutter AP upgrade - deploying this breaks the AP.
|
||||
// if($this->version == 'gamma') {
|
||||
if($this->version == 'gamma') {
|
||||
|
||||
// $this->regions->EU->subregions->IS = new \stdClass();
|
||||
// $this->regions->EU->subregions->IS->tax_rate = 24;
|
||||
// $this->regions->EU->subregions->IS->tax_name = 'VSK';
|
||||
// $this->regions->EU->subregions->IS->reduced_tax_rate = 11;
|
||||
// $this->regions->EU->subregions->IS->apply_tax = false;
|
||||
$this->regions->EU->subregions->IS = new \stdClass();
|
||||
$this->regions->EU->subregions->IS->tax_rate = 24;
|
||||
$this->regions->EU->subregions->IS->tax_name = 'VSK';
|
||||
$this->regions->EU->subregions->IS->reduced_tax_rate = 11;
|
||||
$this->regions->EU->subregions->IS->apply_tax = false;
|
||||
$this->regions->EU->subregions->IS->vat_number = '';
|
||||
|
||||
// $this->regions->EU->subregions->LI = new \stdClass();
|
||||
// $this->regions->EU->subregions->LI->tax_rate = 8.1;
|
||||
// $this->regions->EU->subregions->LI->tax_name = 'MWST';
|
||||
// $this->regions->EU->subregions->LI->reduced_tax_rate = 2.6;
|
||||
// $this->regions->EU->subregions->LI->apply_tax = false;
|
||||
$this->regions->EU->subregions->LI = new \stdClass();
|
||||
$this->regions->EU->subregions->LI->tax_rate = 8.1;
|
||||
$this->regions->EU->subregions->LI->tax_name = 'MWST';
|
||||
$this->regions->EU->subregions->LI->reduced_tax_rate = 2.6;
|
||||
$this->regions->EU->subregions->LI->apply_tax = false;
|
||||
$this->regions->EU->subregions->LI->vat_number = '';
|
||||
|
||||
// $this->regions->EU->subregions->NO = new \stdClass();
|
||||
// $this->regions->EU->subregions->NO->tax_rate = 25;
|
||||
// $this->regions->EU->subregions->NO->tax_name = 'MVA';
|
||||
// $this->regions->EU->subregions->NO->reduced_tax_rate = 12;
|
||||
// $this->regions->EU->subregions->NO->apply_tax = false;
|
||||
$this->regions->EU->subregions->NO = new \stdClass();
|
||||
$this->regions->EU->subregions->NO->tax_rate = 25;
|
||||
$this->regions->EU->subregions->NO->tax_name = 'MVA';
|
||||
$this->regions->EU->subregions->NO->reduced_tax_rate = 12;
|
||||
$this->regions->EU->subregions->NO->apply_tax = false;
|
||||
$this->regions->EU->subregions->NO->vat_number = '';
|
||||
|
||||
// $this->ukRegion();
|
||||
$this->ukRegion();
|
||||
$this->stubVatNumbersOnSubregions();
|
||||
|
||||
// $this->version = 'delta';
|
||||
$this->version = 'delta';
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
@ -138,7 +142,6 @@ class TaxModel
|
|||
$this->usRegion()
|
||||
->euRegion()
|
||||
->auRegion();
|
||||
// ->ukRegion();
|
||||
|
||||
|
||||
return $this->regions;
|
||||
|
|
@ -160,6 +163,7 @@ class TaxModel
|
|||
$this->regions->UK->subregions->GB->tax_name = 'VAT';
|
||||
$this->regions->UK->subregions->GB->reduced_tax_rate = 5;
|
||||
$this->regions->UK->subregions->GB->apply_tax = false;
|
||||
$this->regions->UK->subregions->GB->vat_number = '';
|
||||
|
||||
// Northern Ireland (special case due to NI Protocol)
|
||||
$this->regions->UK->subregions->{'GB-NIR'} = new \stdClass();
|
||||
|
|
@ -167,6 +171,7 @@ class TaxModel
|
|||
$this->regions->UK->subregions->{'GB-NIR'}->tax_name = 'VAT';
|
||||
$this->regions->UK->subregions->{'GB-NIR'}->reduced_tax_rate = 5;
|
||||
$this->regions->UK->subregions->{'GB-NIR'}->apply_tax = false;
|
||||
$this->regions->UK->subregions->{'GB-NIR'}->vat_number = '';
|
||||
|
||||
// Isle of Man (follows UK VAT rules)
|
||||
$this->regions->UK->subregions->{'IM'} = new \stdClass();
|
||||
|
|
@ -174,11 +179,113 @@ class TaxModel
|
|||
$this->regions->UK->subregions->{'IM'}->tax_name = 'VAT';
|
||||
$this->regions->UK->subregions->{'IM'}->reduced_tax_rate = 5;
|
||||
$this->regions->UK->subregions->{'IM'}->apply_tax = false;
|
||||
$this->regions->UK->subregions->{'IM'}->vat_number = '';
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function stubVatNumbersOnSubregions(): self
|
||||
{
|
||||
|
||||
// US Subregions
|
||||
$this->regions->US->subregions->AL->vat_number = '';
|
||||
$this->regions->US->subregions->AK->vat_number = '';
|
||||
$this->regions->US->subregions->AZ->vat_number = '';
|
||||
$this->regions->US->subregions->AR->vat_number = '';
|
||||
$this->regions->US->subregions->CA->vat_number = '';
|
||||
$this->regions->US->subregions->CO->vat_number = '';
|
||||
$this->regions->US->subregions->CT->vat_number = '';
|
||||
$this->regions->US->subregions->DE->vat_number = '';
|
||||
$this->regions->US->subregions->FL->vat_number = '';
|
||||
$this->regions->US->subregions->GA->vat_number = '';
|
||||
$this->regions->US->subregions->HI->vat_number = '';
|
||||
$this->regions->US->subregions->ID->vat_number = '';
|
||||
$this->regions->US->subregions->IL->vat_number = '';
|
||||
$this->regions->US->subregions->IN->vat_number = '';
|
||||
$this->regions->US->subregions->IA->vat_number = '';
|
||||
$this->regions->US->subregions->KS->vat_number = '';
|
||||
$this->regions->US->subregions->KY->vat_number = '';
|
||||
$this->regions->US->subregions->LA->vat_number = '';
|
||||
$this->regions->US->subregions->ME->vat_number = '';
|
||||
$this->regions->US->subregions->MD->vat_number = '';
|
||||
$this->regions->US->subregions->MA->vat_number = '';
|
||||
$this->regions->US->subregions->MI->vat_number = '';
|
||||
$this->regions->US->subregions->MN->vat_number = '';
|
||||
$this->regions->US->subregions->MS->vat_number = '';
|
||||
$this->regions->US->subregions->MO->vat_number = '';
|
||||
$this->regions->US->subregions->MT->vat_number = '';
|
||||
$this->regions->US->subregions->NE->vat_number = '';
|
||||
$this->regions->US->subregions->NV->vat_number = '';
|
||||
$this->regions->US->subregions->NH->vat_number = '';
|
||||
$this->regions->US->subregions->NJ->vat_number = '';
|
||||
$this->regions->US->subregions->NM->vat_number = '';
|
||||
$this->regions->US->subregions->NY->vat_number = '';
|
||||
$this->regions->US->subregions->NC->vat_number = '';
|
||||
$this->regions->US->subregions->ND->vat_number = '';
|
||||
$this->regions->US->subregions->OH->vat_number = '';
|
||||
$this->regions->US->subregions->OK->vat_number = '';
|
||||
$this->regions->US->subregions->OR->vat_number = '';
|
||||
$this->regions->US->subregions->PA->vat_number = '';
|
||||
$this->regions->US->subregions->RI->vat_number = '';
|
||||
$this->regions->US->subregions->SC->vat_number = '';
|
||||
$this->regions->US->subregions->SD->vat_number = '';
|
||||
$this->regions->US->subregions->TN->vat_number = '';
|
||||
$this->regions->US->subregions->TX->vat_number = '';
|
||||
$this->regions->US->subregions->UT->vat_number = '';
|
||||
$this->regions->US->subregions->VT->vat_number = '';
|
||||
$this->regions->US->subregions->VA->vat_number = '';
|
||||
$this->regions->US->subregions->WA->vat_number = '';
|
||||
$this->regions->US->subregions->WV->vat_number = '';
|
||||
$this->regions->US->subregions->WI->vat_number = '';
|
||||
$this->regions->US->subregions->WY->vat_number = '';
|
||||
|
||||
// EU Subregions
|
||||
$this->regions->EU->subregions->AT->vat_number = '';
|
||||
$this->regions->EU->subregions->BE->vat_number = '';
|
||||
$this->regions->EU->subregions->BG->vat_number = '';
|
||||
$this->regions->EU->subregions->CY->vat_number = '';
|
||||
$this->regions->EU->subregions->CZ->vat_number = '';
|
||||
$this->regions->EU->subregions->DE->vat_number = '';
|
||||
$this->regions->EU->subregions->DK->vat_number = '';
|
||||
$this->regions->EU->subregions->EE->vat_number = '';
|
||||
$this->regions->EU->subregions->ES->vat_number = '';
|
||||
$this->regions->EU->subregions->{'ES-CE'}->vat_number = '';
|
||||
$this->regions->EU->subregions->{'ES-ML'}->vat_number = '';
|
||||
$this->regions->EU->subregions->{'ES-CN'}->vat_number = '';
|
||||
$this->regions->EU->subregions->FI->vat_number = '';
|
||||
$this->regions->EU->subregions->FR->vat_number = '';
|
||||
$this->regions->EU->subregions->GR->vat_number = '';
|
||||
$this->regions->EU->subregions->HR->vat_number = '';
|
||||
$this->regions->EU->subregions->HU->vat_number = '';
|
||||
$this->regions->EU->subregions->IE->vat_number = '';
|
||||
$this->regions->EU->subregions->IS->vat_number = '';
|
||||
$this->regions->EU->subregions->IT->vat_number = '';
|
||||
$this->regions->EU->subregions->LI->vat_number = '';
|
||||
$this->regions->EU->subregions->LT->vat_number = '';
|
||||
$this->regions->EU->subregions->LU->vat_number = '';
|
||||
$this->regions->EU->subregions->LV->vat_number = '';
|
||||
$this->regions->EU->subregions->MT->vat_number = '';
|
||||
$this->regions->EU->subregions->NO->vat_number = '';
|
||||
$this->regions->EU->subregions->NL->vat_number = '';
|
||||
$this->regions->EU->subregions->PL->vat_number = '';
|
||||
$this->regions->EU->subregions->PT->vat_number = '';
|
||||
$this->regions->EU->subregions->RO->vat_number = '';
|
||||
$this->regions->EU->subregions->SE->vat_number = '';
|
||||
$this->regions->EU->subregions->SI->vat_number = '';
|
||||
$this->regions->EU->subregions->SK->vat_number = '';
|
||||
|
||||
// UK Subregions
|
||||
$this->regions->UK->subregions->GB->vat_number = '';
|
||||
$this->regions->UK->subregions->{'GB-NIR'}->vat_number = '';
|
||||
$this->regions->UK->subregions->IM->vat_number = '';
|
||||
|
||||
// AU Subregions
|
||||
$this->regions->AU->subregions->AU->vat_number = '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the model for Australian Taxes
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\Socket;
|
||||
|
||||
use App\Models\User;
|
||||
use League\Fractal\Manager;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use App\Utils\Traits\Invoice\Broadcasting\DefaultResourceBroadcast;
|
||||
|
||||
/**
|
||||
* Class DownloadAvailable.
|
||||
*/
|
||||
class DownloadAvailable implements ShouldBroadcast
|
||||
{
|
||||
use SerializesModels;
|
||||
use InteractsWithSockets;
|
||||
|
||||
public function __construct(public string $url, public string $message, public User $user)
|
||||
{
|
||||
}
|
||||
|
||||
public function broadcastOn()
|
||||
{
|
||||
return [
|
||||
new PrivateChannel("user-{$this->user->account->key}-{$this->user->id}"),
|
||||
];
|
||||
}
|
||||
|
||||
public function broadcastWith(): array
|
||||
{
|
||||
|
||||
ctrans('texts.document_download_subject');
|
||||
|
||||
return [
|
||||
'message' => $this->message,
|
||||
'url' => $this->url,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -218,6 +218,14 @@ class ExpenseFilters extends QueryFilters
|
|||
->whereColumn('clients.id', 'expenses.client_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
|
||||
if ($sort_col[0] == 'project' && in_array($sort_col[1], ['asc', 'desc'])) {
|
||||
return $this->builder
|
||||
->orderByRaw('ISNULL(project_id), project_id '. $sort_col[1])
|
||||
->orderBy(\App\Models\Project::select('name')
|
||||
->whereColumn('projects.id', 'expenses.project_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
if ($sort_col[0] == 'vendor_id' && in_array($sort_col[1], ['asc', 'desc'])) {
|
||||
return $this->builder
|
||||
->orderByRaw('ISNULL(vendor_id), vendor_id '. $sort_col[1])
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ class EpcQrGenerator
|
|||
private function validateFields()
|
||||
{
|
||||
if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2)) {
|
||||
nlog('The BIC field is not present and _may_ be a required fields for EPC QR codes');
|
||||
// nlog('The BIC field is not present and _may_ be a required fields for EPC QR codes');
|
||||
}
|
||||
|
||||
if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company1)) {
|
||||
nlog('The IBAN field is required');
|
||||
// nlog('The IBAN field is required');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,11 +242,11 @@ class InvoiceItemSum
|
|||
private function setDiscount()
|
||||
{
|
||||
if ($this->invoice->is_amount_discount) {
|
||||
$this->setLineTotal($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision));
|
||||
$this->setLineTotal(round($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision),2));
|
||||
} else {
|
||||
$discount = ($this->item->line_total * ($this->item->discount / 100));
|
||||
|
||||
$this->setLineTotal($this->formatValue(($this->getLineTotal() - $discount), $this->currency->precision));
|
||||
$this->setLineTotal(round($this->formatValue(($this->getLineTotal() - $discount), $this->currency->precision),2));
|
||||
}
|
||||
|
||||
$this->item->is_amount_discount = $this->invoice->is_amount_discount;
|
||||
|
|
@ -488,6 +488,8 @@ class InvoiceItemSum
|
|||
$amount = $this->item->line_total;
|
||||
}
|
||||
|
||||
// $amount = round($amount,2);
|
||||
|
||||
$item_tax_rate1_total = $this->calcAmountLineTax($this->item->tax_rate1, $amount);
|
||||
|
||||
$item_tax += $item_tax_rate1_total;
|
||||
|
|
|
|||
|
|
@ -177,9 +177,9 @@ class InvoiceItemSumInclusive
|
|||
private function setDiscount()
|
||||
{
|
||||
if ($this->invoice->is_amount_discount) {
|
||||
$this->setLineTotal($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision));
|
||||
$this->setLineTotal(round($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision),2));
|
||||
} else {
|
||||
$this->setLineTotal($this->getLineTotal() - $this->formatValue(($this->item->line_total * ($this->item->discount / 100)), $this->currency->precision));
|
||||
$this->setLineTotal(round($this->getLineTotal() - $this->formatValue(($this->item->line_total * ($this->item->discount / 100)), $this->currency->precision),2));
|
||||
}
|
||||
|
||||
$this->item->is_amount_discount = $this->invoice->is_amount_discount;
|
||||
|
|
|
|||
|
|
@ -130,22 +130,10 @@ class ActivityController extends BaseController
|
|||
$backup = $activity->backup;
|
||||
$html_backup = '';
|
||||
|
||||
/* Refactor 20-10-2021
|
||||
*
|
||||
* We have moved the backups out of the database and into object storage.
|
||||
* In order to handle edge cases, we still check for the database backup
|
||||
* in case the file no longer exists
|
||||
*/
|
||||
$file = $backup->getFile();
|
||||
|
||||
if ($backup && $backup->filename && Storage::disk(config('filesystems.default'))->exists($backup->filename)) { //disk
|
||||
if (Ninja::isHosted()) {
|
||||
$html_backup = file_get_contents(Storage::disk(config('filesystems.default'))->url($backup->filename));
|
||||
} else {
|
||||
$html_backup = file_get_contents(Storage::disk(config('filesystems.default'))->path($backup->filename));
|
||||
}
|
||||
} else { //failed
|
||||
if(!$file)
|
||||
return response()->json(['message' => ctrans('texts.no_backup_exists'), 'errors' => new stdClass()], 404);
|
||||
}
|
||||
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
$pdf = (new Phantom())->convertHtmlToPdf($html_backup);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
|||
|
||||
use App\Events\Contact\ContactLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Contact\ContactLoginRequest;
|
||||
use App\Http\ViewComposers\PortalComposer;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
|
|
@ -36,54 +37,64 @@ class ContactLoginController extends Controller
|
|||
$this->middleware('guest:contact', ['except' => ['logout']]);
|
||||
}
|
||||
|
||||
private function resolveCompany($request, $company_key)
|
||||
{
|
||||
|
||||
if($company_key && MultiDB::findAndSetDbByCompanyKey($company_key))
|
||||
return Company::where('company_key', $company_key)->first();
|
||||
|
||||
$domain_name = $request->getHost();
|
||||
|
||||
if (strpos($domain_name, config('ninja.app_domain')) !== false) {
|
||||
$subdomain = explode('.', $domain_name)[0];
|
||||
|
||||
$query = ['subdomain' => $subdomain];
|
||||
|
||||
if($company = MultiDB::findAndSetDbByDomain($query))
|
||||
return $company;
|
||||
}
|
||||
|
||||
$query = [
|
||||
'portal_domain' => $request->getSchemeAndHttpHost(),
|
||||
'portal_mode' => 'domain',
|
||||
];
|
||||
|
||||
if ($company = MultiDB::findAndSetDbByDomain($query)) {
|
||||
return $company;
|
||||
}
|
||||
|
||||
if(Ninja::isSelfHost())
|
||||
return Company::first();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function showLoginForm(Request $request, $company_key = false)
|
||||
{
|
||||
$company = false;
|
||||
$account = false;
|
||||
$intended = $request->query('intended') ?: false;
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
if ($request->query('intended')) {
|
||||
$request->session()->put('url.intended', $request->query('intended'));
|
||||
if ($intended) {
|
||||
$request->session()->put('url.intended', $intended);
|
||||
}
|
||||
|
||||
if ($request->session()->has('company_key')) {
|
||||
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
|
||||
$company = Company::where('company_key', $request->session()->get('company_key'))->first();
|
||||
} elseif ($request->has('company_key')) {
|
||||
MultiDB::findAndSetDbByCompanyKey($request->input('company_key'));
|
||||
$company = Company::where('company_key', $request->input('company_key'))->first();
|
||||
} elseif ($company_key) {
|
||||
MultiDB::findAndSetDbByCompanyKey($company_key);
|
||||
$company = Company::where('company_key', $company_key)->first();
|
||||
}
|
||||
$company = $this->resolveCompany($request, $company_key);
|
||||
|
||||
/** @var ?\App\Models\Company $company **/
|
||||
if ($company) {
|
||||
$account = $company->account;
|
||||
} elseif (! $company && strpos($request->getHost(), config('ninja.app_domain')) !== false) {
|
||||
$subdomain = explode('.', $request->getHost())[0];
|
||||
MultiDB::findAndSetDbByDomain(['subdomain' => $subdomain]);
|
||||
$company = Company::where('subdomain', $subdomain)->first();
|
||||
} elseif (Ninja::isHosted()) {
|
||||
MultiDB::findAndSetDbByDomain(['portal_domain' => $request->getSchemeAndHttpHost()]);
|
||||
|
||||
$company = Company::where('portal_domain', $request->getSchemeAndHttpHost())->first();
|
||||
} elseif (Ninja::isSelfHost()) {
|
||||
/** @var \App\Models\Account $account **/
|
||||
$account = Account::first();
|
||||
$company = $account->default_company;
|
||||
} else {
|
||||
$company = null;
|
||||
}
|
||||
|
||||
if (! $account) {
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
else {
|
||||
abort(404, "We could not find this site, if you think this is an error, please contact the administrator.");
|
||||
}
|
||||
|
||||
return $this->render('auth.login', ['account' => $account, 'company' => $company]);
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
public function login(ContactLoginRequest $request)
|
||||
{
|
||||
|
||||
Auth::shouldUse('contact');
|
||||
|
|
@ -171,6 +182,7 @@ class ContactLoginController extends Controller
|
|||
{
|
||||
Auth::guard('contact')->logout();
|
||||
request()->session()->invalidate();
|
||||
request()->session()->regenerateToken();
|
||||
|
||||
return redirect('/client/login');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,11 +44,34 @@ class ContactRegisterController extends Controller
|
|||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($company->settings));
|
||||
|
||||
return render('auth.register', ['register_company' => $company, 'account' => $company->account, 'submitsForm' => false]);
|
||||
$domain_name = request()->getHost();
|
||||
|
||||
$show_turnstile = false;
|
||||
|
||||
if (config('ninja.cloudflare.turnstile.site_key') && strpos($domain_name, config('ninja.app_domain')) !== false) {
|
||||
$show_turnstile = true;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'formed_disabled' => $company->account->isFreeHostedClient(),
|
||||
'register_company' => $company,
|
||||
'account' => $company->account,
|
||||
'submitsForm' => false,
|
||||
'show_turnstile' => $show_turnstile
|
||||
];
|
||||
|
||||
return render('auth.register', $data);
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request)
|
||||
{
|
||||
|
||||
$company = $request->company();
|
||||
|
||||
if (! $company->client_can_register || $company->account->isFreeHostedClient()) {
|
||||
abort(403, 'This page is restricted');
|
||||
}
|
||||
|
||||
$request->merge(['company' => $request->company()]);
|
||||
|
||||
$service = new ClientRegisterService(
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class PaymentController extends Controller
|
|||
$payment_hash->payment_id = $payment->id;
|
||||
$payment_hash->save();
|
||||
}
|
||||
|
||||
$payment->type_id = PaymentType::CREDIT;
|
||||
$payment = $payment->service()->applyCredits($payment_hash)->save();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Invoice> $invoices */
|
||||
|
|
|
|||
|
|
@ -570,6 +570,7 @@ class DesignController extends BaseController
|
|||
case 'invoice':
|
||||
|
||||
$company->invoices()
|
||||
->withTrashed()
|
||||
->when($settings_level == 'company', function ($query) {
|
||||
$query->where(function ($query) {
|
||||
$query->whereDoesntHave('client.group_settings')
|
||||
|
|
@ -592,18 +593,19 @@ class DesignController extends BaseController
|
|||
|
||||
$query->where('client_id', $client_id);
|
||||
|
||||
})
|
||||
->update(['design_id' => $design_id]);
|
||||
|
||||
})->update(['design_id' => $design_id]);
|
||||
|
||||
|
||||
// Recurring Invoice Designs are set using the global company level.
|
||||
if ($settings_level == 'company') {
|
||||
$company->recurring_invoices()->update(['design_id' => $design_id]);
|
||||
$company->recurring_invoices()->withTrashed()->update(['design_id' => $design_id]);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'quote':
|
||||
|
||||
$company->quotes()
|
||||
->withTrashed()
|
||||
->when($settings_level == 'company', function ($query) {
|
||||
$query->where(function ($query) {
|
||||
$query->whereDoesntHave('client.group_settings')
|
||||
|
|
@ -633,6 +635,7 @@ class DesignController extends BaseController
|
|||
case 'credit':
|
||||
|
||||
$company->credits()
|
||||
->withTrashed()
|
||||
->when($settings_level == 'company', function ($query) {
|
||||
$query->where(function ($query) {
|
||||
$query->whereDoesntHave('client.group_settings')
|
||||
|
|
@ -661,10 +664,10 @@ class DesignController extends BaseController
|
|||
break;
|
||||
|
||||
case 'purchase_order':
|
||||
$company->purchase_orders()->update(['design_id' => $design_id]);
|
||||
$company->purchase_orders()->withTrashed()->update(['design_id' => $design_id]);
|
||||
break;
|
||||
case 'recurring_invoice':
|
||||
$company->recurring_invoices()->update(['design_id' => $design_id]);
|
||||
$company->recurring_invoices()->withTrashed()->update(['design_id' => $design_id]);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -64,7 +64,11 @@ class EInvoiceController extends BaseController
|
|||
{
|
||||
$einvoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice();
|
||||
|
||||
foreach ($request->input('payment_means', []) as $payment_means) {
|
||||
$payment_means_array = $request->input('payment_means', []);
|
||||
|
||||
$einvoice->PaymentMeans = [];
|
||||
|
||||
foreach ($payment_means_array as $payment_means) {
|
||||
$pm = new PaymentMeans();
|
||||
|
||||
$pmc = new PaymentMeansCode();
|
||||
|
|
@ -102,9 +106,12 @@ class EInvoiceController extends BaseController
|
|||
$pm->InstructionNote = $payment_means['information'];
|
||||
}
|
||||
|
||||
// nlog($pm);
|
||||
$einvoice->PaymentMeans[] = $pm;
|
||||
}
|
||||
|
||||
// nlog($einvoice);
|
||||
|
||||
$stub = new \stdClass();
|
||||
$stub->Invoice = $einvoice;
|
||||
|
||||
|
|
@ -137,7 +144,6 @@ class EInvoiceController extends BaseController
|
|||
'account_key' => $company->account->key,
|
||||
]);
|
||||
|
||||
|
||||
if ($response->status() == 422) {
|
||||
return response()->json(['message' => $response->json('message')], 422);
|
||||
}
|
||||
|
|
@ -146,8 +152,13 @@ class EInvoiceController extends BaseController
|
|||
return response()->json(['message' => $response->json('message')], 400);
|
||||
}
|
||||
|
||||
$account = $company->account;
|
||||
|
||||
$account->e_invoice_quota = (int) $response->body();
|
||||
$account->save();
|
||||
|
||||
return response()->json([
|
||||
'quota' => $response->body(),
|
||||
'quota' => $account->e_invoice_quota,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ use App\Services\EDocument\Gateway\Storecove\Storecove;
|
|||
use App\Http\Requests\EInvoice\Peppol\DisconnectRequest;
|
||||
use App\Http\Requests\EInvoice\Peppol\AddTaxIdentifierRequest;
|
||||
use App\Http\Requests\EInvoice\Peppol\RemoveTaxIdentifierRequest;
|
||||
use App\Http\Requests\EInvoice\Peppol\RetrySendRequest;
|
||||
use App\Http\Requests\EInvoice\Peppol\ShowEntityRequest;
|
||||
use App\Http\Requests\EInvoice\Peppol\UpdateEntityRequest;
|
||||
use App\Services\EDocument\Jobs\SendEDocument;
|
||||
|
||||
class EInvoicePeppolController extends BaseController
|
||||
{
|
||||
|
|
@ -59,7 +61,7 @@ class EInvoicePeppolController extends BaseController
|
|||
->setup($request->validated());
|
||||
|
||||
if (data_get($response, 'status') === 'error') {
|
||||
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
|
||||
return response()->json(data_get($response, 'message'), status: $response['code']);
|
||||
}
|
||||
|
||||
$company->legal_entity_id = $response['legal_entity_id'];
|
||||
|
|
@ -113,7 +115,7 @@ class EInvoicePeppolController extends BaseController
|
|||
->updateLegalEntity($request->validated());
|
||||
|
||||
if (data_get($response, 'status') === 'error') {
|
||||
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
|
||||
return response()->json(data_get($response, 'message'), status: $response['code']);
|
||||
}
|
||||
|
||||
$tax_data = $company->tax_data;
|
||||
|
|
@ -147,7 +149,7 @@ class EInvoicePeppolController extends BaseController
|
|||
->disconnect();
|
||||
|
||||
if (data_get($response, 'status') === 'error') {
|
||||
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
|
||||
return response()->json(data_get($response, 'message'), status: $response['code']);
|
||||
}
|
||||
|
||||
$company->legal_entity_id = null;
|
||||
|
|
@ -198,7 +200,7 @@ class EInvoicePeppolController extends BaseController
|
|||
->addAdditionalTaxIdentifier($request->validated());
|
||||
|
||||
if (data_get($response, 'status') === 'error') {
|
||||
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
|
||||
return response()->json(data_get($response, 'message'), status: $response['code']);
|
||||
}
|
||||
|
||||
if ($country == 'GB') {
|
||||
|
|
@ -225,7 +227,7 @@ class EInvoicePeppolController extends BaseController
|
|||
->removeAdditionalTaxIdentifier($request->validated());
|
||||
|
||||
if (data_get($response, 'status') === 'error') {
|
||||
return response()->json(data_get($response, 'errors', 'message'), status: $response['code']);
|
||||
return response()->json(data_get($response, 'message'), status: $response['code']);
|
||||
}
|
||||
|
||||
if (is_bool($response)) {
|
||||
|
|
@ -251,6 +253,14 @@ class EInvoicePeppolController extends BaseController
|
|||
return response()->json([]);
|
||||
}
|
||||
|
||||
public function retrySend(RetrySendRequest $request)
|
||||
{
|
||||
|
||||
SendEDocument::dispatch($request->entity, $request->entity_id, auth()->user()->company()->db);
|
||||
|
||||
return response()->json(['message' => 'trying....'], 200);
|
||||
}
|
||||
|
||||
private function unsetVatNumbers(mixed $taxData): mixed
|
||||
{
|
||||
if (isset($taxData->regions->EU->subregions)) {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class EmailController extends BaseController
|
|||
$this->entity_transformer = InvoiceTransformer::class;
|
||||
|
||||
if ($entity_obj->invitations->count() >= 1) {
|
||||
$entity_obj->entityEmailEvent($entity_obj->invitations->first(), 'invoice', $template);
|
||||
$entity_obj->entityEmailEvent($entity_obj->invitations->first(), $template, $template);
|
||||
$entity_obj->sendEvent(Webhook::EVENT_SENT_INVOICE, "client");
|
||||
}
|
||||
}
|
||||
|
|
@ -112,9 +112,8 @@ class EmailController extends BaseController
|
|||
$this->entity_transformer = QuoteTransformer::class;
|
||||
|
||||
if ($entity_obj->invitations->count() >= 1) {
|
||||
event(new QuoteWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'quote'));
|
||||
$entity_obj->entityEmailEvent($entity_obj->invitations->first(), $template);
|
||||
$entity_obj->sendEvent(Webhook::EVENT_SENT_QUOTE, "client");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class PostMarkController extends BaseController
|
|||
public function webhook(Request $request)
|
||||
{
|
||||
if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token')) {
|
||||
ProcessPostmarkWebhook::dispatch($request->all())->delay(rand(6, 14));
|
||||
ProcessPostmarkWebhook::dispatch($request->all())->delay(15);
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,19 @@ class SearchController extends Controller
|
|||
private array $invoices = [];
|
||||
|
||||
private array $quotes = [];
|
||||
|
||||
private array $expenses = [];
|
||||
|
||||
private array $credits = [];
|
||||
|
||||
private array $recurring_invoices = [];
|
||||
|
||||
private array $vendors = [];
|
||||
|
||||
private array $vendor_contacts = [];
|
||||
|
||||
private array $purchase_orders = [];
|
||||
|
||||
|
||||
public function __invoke(GenericSearchRequest $request)
|
||||
{
|
||||
|
|
@ -42,6 +55,7 @@ class SearchController extends Controller
|
|||
$user = auth()->user();
|
||||
|
||||
$this->clientMap($user);
|
||||
|
||||
$this->invoiceMap($user);
|
||||
|
||||
return response()->json([
|
||||
|
|
@ -64,6 +78,7 @@ class SearchController extends Controller
|
|||
|
||||
$params = [
|
||||
'index' => 'clients,invoices,client_contacts',
|
||||
// 'index' => 'clients,invoices,client_contacts,quotes,expenses,credits,recurring_invoices,vendors,vendor_contacts,purchase_orders',
|
||||
'body' => [
|
||||
'query' => [
|
||||
'bool' => [
|
||||
|
|
@ -93,6 +108,14 @@ class SearchController extends Controller
|
|||
'clients' => $this->clients,
|
||||
'client_contacts' => $this->client_contacts,
|
||||
'invoices' => $this->invoices,
|
||||
'quotes' => $this->quotes,
|
||||
|
||||
'expenses' => $this->expenses,
|
||||
'credits' => $this->credits,
|
||||
'recurring_invoices' => $this->recurring_invoices,
|
||||
'vendors' => $this->vendors,
|
||||
'vendor_contacts' => $this->vendor_contacts,
|
||||
'purchase_orders' => $this->purchase_orders,
|
||||
'settings' => $this->settingsMap(),
|
||||
], 200);
|
||||
|
||||
|
|
@ -133,7 +156,7 @@ class SearchController extends Controller
|
|||
break;
|
||||
case 'client_contacts':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) { // do not return deleted contacts
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +169,7 @@ class SearchController extends Controller
|
|||
break;
|
||||
case 'quotes':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) { // do not return deleted contacts
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +181,97 @@ class SearchController extends Controller
|
|||
];
|
||||
|
||||
break;
|
||||
|
||||
case 'expenses':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->expenses[] = [
|
||||
'name' => $result['_source']['name'],
|
||||
'type' => '/expense',
|
||||
'id' => $result['_source']['hashed_id'],
|
||||
'path' => "/expenses/{$result['_source']['hashed_id']}"
|
||||
];
|
||||
|
||||
break;
|
||||
|
||||
case 'credits':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->credits[] = [
|
||||
'name' => $result['_source']['name'],
|
||||
'type' => '/credit',
|
||||
'id' => $result['_source']['hashed_id'],
|
||||
'path' => "/credits/{$result['_source']['hashed_id']}"
|
||||
];
|
||||
|
||||
break;
|
||||
|
||||
case 'recurring_invoices':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->recurring_invoices[] = [
|
||||
'name' => $result['_source']['name'],
|
||||
'type' => '/recurring_invoice',
|
||||
'id' => $result['_source']['hashed_id'],
|
||||
'path' => "/recurring_invoices/{$result['_source']['hashed_id']}"
|
||||
];
|
||||
|
||||
break;
|
||||
|
||||
case 'vendors':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->vendors[] = [
|
||||
'name' => $result['_source']['name'],
|
||||
'type' => '/vendor',
|
||||
'id' => $result['_source']['hashed_id'],
|
||||
'path' => "/vendors/{$result['_source']['hashed_id']}"
|
||||
];
|
||||
|
||||
break;
|
||||
|
||||
case 'vendor_contacts':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->vendor_contacts[] = [
|
||||
'name' => $result['_source']['name'],
|
||||
'type' => '/client',
|
||||
'id' => $result['_source']['hashed_id'],
|
||||
'path' => "/clients/{$result['_source']['hashed_id']}"
|
||||
];
|
||||
|
||||
break;
|
||||
|
||||
case 'purchase_orders':
|
||||
|
||||
if ($result['_source']['__soft_deleted']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->purchase_orders[] = [
|
||||
'name' => $result['_source']['name'],
|
||||
'type' => '/purchase_order',
|
||||
'id' => $result['_source']['hashed_id'],
|
||||
'path' => "/purchase_orders/{$result['_source']['hashed_id']}"
|
||||
];
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class SetDomainNameDb
|
|||
MultiDB::setDb('db-ninja-01');
|
||||
nlog('SetDomainNameDb:: I could not set the DB - defaulting to DB1');
|
||||
$request->session()->invalidate();
|
||||
//abort(400, 'Domain not found');
|
||||
$request->session()->regenerateToken();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -76,6 +76,7 @@ class SetDomainNameDb
|
|||
MultiDB::setDb('db-ninja-01');
|
||||
nlog('SetDomainNameDb:: I could not set the DB - defaulting to DB1');
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\ClientPortal\Contact;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ContactLoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'required',
|
||||
'password' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -11,12 +11,13 @@
|
|||
|
||||
namespace App\Http\Requests\ClientPortal;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use App\Http\ValidationRules\Turnstile\Turnstile;
|
||||
|
||||
class RegisterRequest extends FormRequest
|
||||
{
|
||||
|
|
@ -59,6 +60,8 @@ class RegisterRequest extends FormRequest
|
|||
$rules['terms'] = ['required'];
|
||||
}
|
||||
|
||||
$rules['cf-turnstile-response'] = ['sometimes', new Turnstile];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@ class StoreCreditRequest extends Request
|
|||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['date'] = 'bail|sometimes|date:Y-m-d';
|
||||
|
||||
if ($this->invoice_id) {
|
||||
|
|
|
|||
|
|
@ -84,6 +84,11 @@ class UpdateCreditRequest extends Request
|
|||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\EInvoice\Peppol;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class RetrySendRequest extends Request
|
||||
{
|
||||
private string $entity_plural = 'invoices';
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
if (config('ninja.app_env') == 'local') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->account->isPaid() && $user->isAdmin() && $user->company()->legal_entity_id != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
/** @var \App\Models\User $user **/
|
||||
$user = auth()->user();
|
||||
|
||||
return [
|
||||
'entity' => ['bail','required','in:App\Models\Invoice,App\Models\Quote,App\Models\Credit,App\Models\PurchaseOrder'],
|
||||
'entity_id' => ['bail', 'required', Rule::exists($this->entity_plural, 'id')->where('company_id', $user->company()->id)],
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
|
||||
$input = $this->all();
|
||||
|
||||
|
||||
if (array_key_exists('entity_id', $input)) {
|
||||
$input['entity_id'] = $this->decodePrimaryKey($input['entity_id']);
|
||||
}
|
||||
|
||||
if (isset($input['entity']) && in_array($input['entity'], ['invoice','quote','credit','purchase_order'])) {
|
||||
$this->entity_plural = Str::plural($input['entity']);
|
||||
$input['entity'] = "App\Models\\".ucfirst(Str::camel($input['entity']));
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -53,14 +53,14 @@ class UpdateEntityRequest extends FormRequest
|
|||
$this->replace($input);
|
||||
}
|
||||
|
||||
public function after(): array
|
||||
{
|
||||
return [
|
||||
function (Validator $validator) {
|
||||
if ($this->input('acts_as_sender') === false && $this->input('acts_as_receiver') === false) {
|
||||
$validator->errors()->add('acts_as_receiver', ctrans('texts.acts_as_must_be_true'));
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
// public function after(): array
|
||||
// {
|
||||
// return [
|
||||
// function (Validator $validator) {
|
||||
// if ($this->input('acts_as_sender') === false && $this->input('acts_as_receiver') === false) {
|
||||
// $validator->errors()->add('acts_as_receiver', ctrans('texts.acts_as_must_be_true'));
|
||||
// }
|
||||
// }
|
||||
// ];
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class BulkInvoiceRequest extends Request
|
|||
throw new DuplicatePaymentException('Action still processing, please wait. ', 429);
|
||||
}
|
||||
|
||||
$delay = $this->input('action', 'delete') == 'delete' ? (ceil(count($this->input('ids', 4)))) : 1;
|
||||
$delay = $this->input('action', 'delete') == 'delete' ? (ceil(count($this->input('ids', 2)))) : 1;
|
||||
\Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->input('action', 0)."|".$user->company()->company_key), true, $delay);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@ class StoreInvoiceRequest extends Request
|
|||
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,12 @@ class UpdateInvoiceRequest extends Request
|
|||
$rules['partial'] = 'bail|sometimes|nullable|numeric';
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
|
||||
$rules['date'] = 'bail|sometimes|date:Y-m-d';
|
||||
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
|
||||
|
|
|
|||
|
|
@ -145,6 +145,10 @@ class StorePaymentRequest extends Request
|
|||
$input['idempotency_key'] = substr(time()."{$input['date']}{$input['amount']}{$credits_total}{$this->client_id}{$user->company()->company_key}", 0, 64);
|
||||
}
|
||||
|
||||
if (array_key_exists('exchange_rate', $input) && $input['exchange_rate'] === null) {
|
||||
unset($input['exchange_rate']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,11 @@ class StorePurchaseOrderRequest extends Request
|
|||
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,11 @@ class UpdatePurchaseOrderRequest extends Request
|
|||
$rules['status_id'] = 'sometimes|integer|in:1,2,3,4,5';
|
||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,11 @@ class StoreQuoteRequest extends Request
|
|||
$rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,11 @@ class UpdateQuoteRequest extends Request
|
|||
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
|
||||
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
$rules['custom_surcharge1'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge2'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge3'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\ValidationRules\Turnstile;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class Turnstile implements ValidationRule
|
||||
{
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
|
||||
'secret' => config('ninja.cloudflare.turnstile.secret'),
|
||||
'response' => $value,
|
||||
'remoteip' => request()->ip(),
|
||||
]);
|
||||
|
||||
$data = $response->json();
|
||||
|
||||
if($data['success']){
|
||||
|
||||
}
|
||||
else {
|
||||
$fail("Captcha failed");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -34,6 +34,9 @@ class ClientTransformer extends BaseTransformer
|
|||
throw new ImportException('Client already exists');
|
||||
}
|
||||
|
||||
if(!is_array($data))
|
||||
throw new ImportException('Empty row, or invalid data encountered.');
|
||||
|
||||
$settings = ClientSettings::defaults();
|
||||
$settings->currency_id = (string) $this->getCurrencyByCode($data);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ namespace App\Import\Transformer\Csv;
|
|||
use App\Import\ImportException;
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
use App\Models\Invoice;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
|
||||
/**
|
||||
* Class InvoiceTransformer.
|
||||
*/
|
||||
class InvoiceTransformer extends BaseTransformer
|
||||
{
|
||||
use CleanLineItems;
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
|
|
@ -224,7 +226,7 @@ class InvoiceTransformer extends BaseTransformer
|
|||
];
|
||||
}
|
||||
|
||||
$transformed['line_items'] = $line_items;
|
||||
$transformed['line_items'] = $this->cleanItems($line_items);
|
||||
|
||||
return $transformed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,15 @@ namespace App\Import\Transformer\Csv;
|
|||
use App\Import\ImportException;
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
|
||||
/**
|
||||
* Class QuoteTransformer.
|
||||
*/
|
||||
class QuoteTransformer extends BaseTransformer
|
||||
{
|
||||
use CleanLineItems;
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
|
|
@ -120,7 +123,7 @@ class QuoteTransformer extends BaseTransformer
|
|||
$this->getString($quote_data, 'quote.status')
|
||||
))
|
||||
] ?? Quote::STATUS_SENT,
|
||||
'archived' => $status === 'archived',
|
||||
// 'archived' => $status === 'archived',
|
||||
];
|
||||
|
||||
/* If we can't find the client, then lets try and create a client */
|
||||
|
|
@ -221,7 +224,7 @@ class QuoteTransformer extends BaseTransformer
|
|||
'type_id' => '1', //$this->getQuoteTypeId( $record, 'item.type_id' ),
|
||||
];
|
||||
}
|
||||
$transformed['line_items'] = $line_items;
|
||||
$transformed['line_items'] = $this->cleanItems($line_items);
|
||||
|
||||
return $transformed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,15 @@ use App\Import\ImportException;
|
|||
use App\Import\Transformer\BaseTransformer;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceTransformer.
|
||||
*/
|
||||
class RecurringInvoiceTransformer extends BaseTransformer
|
||||
{
|
||||
use CleanLineItems;
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
|
|
@ -187,7 +190,7 @@ class RecurringInvoiceTransformer extends BaseTransformer
|
|||
];
|
||||
}
|
||||
|
||||
$transformed['line_items'] = $line_items;
|
||||
$transformed['line_items'] = $this->cleanItems($line_items);
|
||||
|
||||
return $transformed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,9 @@ class CreateAccount
|
|||
NinjaMailerJob::dispatch($nmo, true);
|
||||
|
||||
(new \Modules\Admin\Jobs\Account\NinjaUser([], $sp035a66))->handle();
|
||||
|
||||
// if($sp794f3f->referral_code && Ninja::isHosted()) //2024-11-29 - pausing on this.
|
||||
// \Modules\Admin\Jobs\Account\NewReferredAccount::dispatch($sp794f3f->key);
|
||||
}
|
||||
|
||||
VersionCheck::dispatch();
|
||||
|
|
|
|||
|
|
@ -51,15 +51,17 @@ class AutoBill implements ShouldQueue
|
|||
if ($this->db) {
|
||||
MultiDB::setDb($this->db);
|
||||
}
|
||||
|
||||
$invoice = false;
|
||||
|
||||
|
||||
try {
|
||||
|
||||
nlog("autobill {$this->invoice_id}");
|
||||
|
||||
|
||||
$invoice = Invoice::withTrashed()->find($this->invoice_id);
|
||||
|
||||
$invoice->service()->autoBill();
|
||||
|
||||
if($invoice)
|
||||
$invoice->service()->autoBill();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
nlog("Failed to capture payment for {$this->invoice_id} ->".$e->getMessage());
|
||||
|
|
@ -67,7 +69,8 @@ class AutoBill implements ShouldQueue
|
|||
if ($this->send_email_on_failure && $invoice) {
|
||||
|
||||
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
||||
if ($invitation->contact && ! $invitation->contact->trashed() && strlen($invitation->contact->email) >= 1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||
|
||||
if ($invitation->contact && !$invitation->contact->trashed() && strlen($invitation->contact->email) >= 1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||
try {
|
||||
EmailEntity::dispatch($invitation->withoutRelations(), $invoice->company->db)->delay(rand(1, 2));
|
||||
|
||||
|
|
@ -77,7 +80,7 @@ class AutoBill implements ShouldQueue
|
|||
nlog($e->getMessage());
|
||||
}
|
||||
|
||||
nlog("Firing email for invoice {$invoice->number}");
|
||||
nlog("Firing email for invoice {$invoice->number} which failed to capture payment");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -59,17 +59,19 @@ class CopyDocs implements ShouldQueue
|
|||
|
||||
$new_hash = \Illuminate\Support\Str::random(32) . "." . $extension;
|
||||
|
||||
$relative_path = "{$this->entity->company->company_key}/documents/{$new_hash}";
|
||||
|
||||
Storage::disk($document->disk)->put(
|
||||
"{$this->entity->company->company_key}/documents/{$new_hash}",
|
||||
$relative_path,
|
||||
$file,
|
||||
);
|
||||
|
||||
$instance = Storage::disk($document->disk)->path("{$this->entity->company->company_key}/documents/{$new_hash}");
|
||||
// $instance = Storage::disk($document->disk)->path("{$this->entity->company->company_key}/documents/{$new_hash}");
|
||||
|
||||
$new_doc = new Document();
|
||||
$new_doc->user_id = $this->entity->user_id;
|
||||
$new_doc->company_id = $this->entity->company_id;
|
||||
$new_doc->url = $instance;
|
||||
$new_doc->url = $relative_path;
|
||||
$new_doc->name = $document->name;
|
||||
$new_doc->type = $extension;
|
||||
$new_doc->disk = $document->disk;
|
||||
|
|
@ -84,4 +86,4 @@ class CopyDocs implements ShouldQueue
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace App\Jobs\Invoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use CleverIt\UBL\Invoice\Address;
|
||||
use CleverIt\UBL\Invoice\Contact;
|
||||
|
|
@ -125,7 +126,7 @@ class CreateUbl implements ShouldQueue
|
|||
|
||||
if ($company->country_id) {
|
||||
$country = new Country();
|
||||
$country->setIdentificationCode($company->country->iso_3166_2);
|
||||
$country->setIdentificationCode(($company instanceof Company) ? $company->country()->iso_3166_2 : $company->country->iso_3166_2);
|
||||
$address->setCountry($country);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,20 +11,22 @@
|
|||
|
||||
namespace App\Jobs\Invoice;
|
||||
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadInvoices;
|
||||
use App\Models\User;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\User;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use App\Mail\DownloadInvoices;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Events\Socket\DownloadAvailable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class ZipInvoices implements ShouldQueue
|
||||
{
|
||||
|
|
@ -55,11 +57,10 @@ class ZipInvoices implements ShouldQueue
|
|||
public function handle(): void
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
App::setLocale($this->company->locale());
|
||||
|
||||
$settings = $this->company->settings;
|
||||
|
||||
nlog(count($this->invoices));
|
||||
|
||||
$this->invoices = Invoice::withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->whereIn('id', $this->invoices)
|
||||
|
|
@ -99,9 +100,10 @@ class ZipInvoices implements ShouldQueue
|
|||
}
|
||||
|
||||
Storage::put($path.$file_name, $zipFile->outputAsString());
|
||||
$storage_url = Storage::url($path.$file_name);
|
||||
|
||||
$nmo = new NinjaMailerObject();
|
||||
$nmo->mailable = new DownloadInvoices(Storage::url($path.$file_name), $this->company);
|
||||
$nmo->mailable = new DownloadInvoices($storage_url, $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->settings = $settings;
|
||||
$nmo->company = $this->company;
|
||||
|
|
@ -110,6 +112,11 @@ class ZipInvoices implements ShouldQueue
|
|||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
|
||||
|
||||
$message = count($this->invoices). " ". ctrans('texts.invoices');
|
||||
$message = ctrans('texts.download_ready', ['message' => $message]);
|
||||
|
||||
broadcast(new DownloadAvailable($storage_url, $message, $this->user));
|
||||
|
||||
} catch (\PhpZip\Exception\ZipException $e) {
|
||||
nlog('could not make zip => '.$e->getMessage());
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -82,11 +82,13 @@ class PaymentFailedMailer implements ShouldQueue
|
|||
|
||||
$amount = 0;
|
||||
$invoice = false;
|
||||
$invitation = false;
|
||||
|
||||
if ($this->payment_hash) {
|
||||
|
||||
$amount = $this->payment_hash?->amount_with_fee() ?: 0;
|
||||
$invoice = Invoice::query()->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
||||
$invoice = Invoice::query()->with('invitations')->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
||||
$invitation = $invoice->invitations->first();
|
||||
}
|
||||
|
||||
//iterate through company_users
|
||||
|
|
@ -97,6 +99,8 @@ class PaymentFailedMailer implements ShouldQueue
|
|||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
$invitation = $invoice->invitations->first();
|
||||
|
||||
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $amount, $this->payment_hash, $company_user->portalType()))->build();
|
||||
|
||||
$nmo = new NinjaMailerObject();
|
||||
|
|
@ -121,6 +125,10 @@ class PaymentFailedMailer implements ShouldQueue
|
|||
$nmo->company = $this->company;
|
||||
$nmo->to_user = $contact;
|
||||
$nmo->settings = $settings;
|
||||
|
||||
if ($invitation) {
|
||||
$nmo->invitation = $invitation->withoutRelations();
|
||||
}
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ class ProcessMailgunWebhook implements ShouldQueue
|
|||
|
||||
$bounce = new EmailBounce(
|
||||
$this->request['event-data']['tags'][0],
|
||||
$this->request['event-data']['envelope']['sender'] ?? $this->request['event-data']['envelope']['from'],
|
||||
$this->request['event-data']['message']['headers']['from'] ?? $this->request['event-data']['message']['headers']['to'],
|
||||
$this->message_id
|
||||
);
|
||||
|
||||
|
|
@ -374,11 +374,11 @@ class ProcessMailgunWebhook implements ShouldQueue
|
|||
|
||||
$event = [
|
||||
'bounce_id' => $this->request['event-data']['id'],
|
||||
'recipient' => $this->request['event-data']['recipient'] ?? '',
|
||||
'recipient' => $this->request['event-data']['message']['headers']['to'] ?? '',
|
||||
'status' => $this->request['event-data']['event'] ?? '',
|
||||
'delivery_message' => $this->request['event-data']['delivery-status']['description'] ?? $this->request['event-data']['delivery-status']['message'] ?? '',
|
||||
'server' => $this->request['event-data']['delivery-status']['mx-host'] ?? '',
|
||||
'server_ip' => $this->request['event-data']['envelope']['sending-ip'] ?? '',
|
||||
'delivery_message' => $this->request['event-data']['delivery-status']['message'] ?? $this->request['event-data']['delivery-status']['bounce-code'] ?? '',
|
||||
'server' => $this->request['event-data']['delivery-status']['message'] ?? '',
|
||||
'server_ip' => '',
|
||||
'date' => \Carbon\Carbon::parse($this->request['event-data']['timestamp'])->format('Y-m-d H:i:s') ?? '',
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,8 @@ class SendReminders implements ShouldQueue
|
|||
nlog('firing email');
|
||||
|
||||
EmailEntity::dispatch($invitation->withoutRelations(), $invitation->company->db, $template)->delay(10);
|
||||
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $template));
|
||||
// event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $template));
|
||||
$invoice->entityEmailEvent($invoice->invitations->first(), $template);
|
||||
$invoice->sendEvent(Webhook::EVENT_REMIND_INVOICE, "client");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class TaskScheduler implements ShouldQueue
|
|||
try {
|
||||
//@var \App\Models\Schedule $scheduler
|
||||
$scheduler->service()->runTask();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
nlog("Exception:: TaskScheduler:: Doing job :: {$scheduler->id} :: {$scheduler->name}" . $e->getMessage());
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ class TaskScheduler implements ShouldQueue
|
|||
try {
|
||||
/** @var \App\Models\Scheduler $scheduler */
|
||||
$scheduler->service()->runTask();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
nlog("Exception:: TaskScheduler:: #{$scheduler->id}::" . $e->getMessage());
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ class SendRecurring implements ShouldQueue
|
|||
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
||||
if ($invitation->contact && ! $invitation->contact->trashed() && strlen($invitation->contact->email) >= 1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||
try {
|
||||
EmailEntity::dispatch($invitation->withoutRelations(), $invoice->company->db)->delay(rand(1, 2));
|
||||
EmailEntity::dispatch($invitation->withoutRelations(), $invoice->company->db, 'invoice')->delay(rand(1, 2));
|
||||
} catch (\Exception $e) {
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ use App\Jobs\Mail\NinjaMailerJob;
|
|||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Services\Report\ARDetailReport;
|
||||
use App\Services\Report\ARSummaryReport;
|
||||
use App\Services\Report\ClientBalanceReport;
|
||||
use App\Services\Report\ClientSalesReport;
|
||||
use App\Services\Report\TaxSummaryReport;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
|
@ -61,10 +64,10 @@ class SendToAdmin implements ShouldQueue
|
|||
$files = [];
|
||||
$files[] = ['file' => $csv, 'file_name' => "{$this->file_name}", 'mime' => 'text/csv'];
|
||||
|
||||
// if(in_array(get_class($export), [ARDetailReport::class, ARSummaryReport::class])) {
|
||||
// $pdf = base64_encode($export->getPdf());
|
||||
// $files[] = ['file' => $pdf, 'file_name' => str_replace(".csv", ".pdf", $this->file_name), 'mime' => 'application/pdf'];
|
||||
// }
|
||||
if(in_array(get_class($export), [ARDetailReport::class, ARSummaryReport::class, ClientBalanceReport::class, ClientSalesReport::class, TaxSummaryReport::class])) {
|
||||
$pdf = base64_encode($export->getPdf());
|
||||
$files[] = ['file' => $pdf, 'file_name' => str_replace(".csv", ".pdf", $this->file_name), 'mime' => 'application/pdf'];
|
||||
}
|
||||
|
||||
$user = $this->company->owner();
|
||||
|
||||
|
|
|
|||
|
|
@ -130,12 +130,8 @@ class QuoteReminderJob implements ShouldQueue
|
|||
nrlog("#{$quote->number} => reminder template = {$reminder_template}");
|
||||
$quote->service()->touchReminder($reminder_template)->save();
|
||||
|
||||
//20-04-2022 fixes for endless reminders - generic template naming was wrong
|
||||
$enabled_reminder = 'enable_quote_'.$reminder_template;
|
||||
// if ($reminder_template == 'endless_reminder') {
|
||||
// $enabled_reminder = 'enable_reminder_endless';
|
||||
// }
|
||||
|
||||
|
||||
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'endless_reminder']) &&
|
||||
$quote->client->getSetting($enabled_reminder) &&
|
||||
$quote->client->getSetting('send_reminders') &&
|
||||
|
|
|
|||
|
|
@ -126,9 +126,11 @@ class ReminderJob implements ShouldQueue
|
|||
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||
nrlog("#{$invoice->number} => reminder template = {$reminder_template}");
|
||||
$invoice->service()->touchReminder($reminder_template)->save();
|
||||
|
||||
$fees = $this->calcLateFee($invoice, $reminder_template);
|
||||
|
||||
if ($invoice->isLocked()) {
|
||||
nlog("invoice is locked - adding fee to new invoice");
|
||||
return $this->addFeeToNewInvoice($invoice, $reminder_template, $fees);
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +287,7 @@ class ReminderJob implements ShouldQueue
|
|||
*/
|
||||
private function setLateFee($invoice, $amount, $percent): Invoice
|
||||
{
|
||||
|
||||
|
||||
$temp_invoice_balance = $invoice->balance;
|
||||
|
||||
if ($amount <= 0 && $percent <= 0) {
|
||||
|
|
|
|||
|
|
@ -53,9 +53,6 @@ class InvoiceEmailedNotification implements ShouldQueue
|
|||
/* The User */
|
||||
$user = $company_user->user;
|
||||
|
||||
/* This is only here to handle the alternate message channels - ie Slack */
|
||||
// $notification = new EntitySentNotification($event->invitation, 'invoice');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent', 'invoice_sent_all', 'invoice_sent_user']);
|
||||
|
||||
|
|
@ -76,11 +73,6 @@ class InvoiceEmailedNotification implements ShouldQueue
|
|||
$first_notification_sent = false;
|
||||
}
|
||||
|
||||
/* Override the methods in the Notification Class */
|
||||
// $notification->method = $methods;
|
||||
|
||||
// Notify on the alternate channels
|
||||
// $user->notify($notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use App\Models\RecurringInvoiceInvitation;
|
|||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Symfony\Component\Mime\MessageConverter;
|
||||
|
||||
class MailSentListener implements ShouldQueue
|
||||
class MailSentListener
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||
class QuoteEmailedNotification implements ShouldQueue
|
||||
{
|
||||
use UserNotifies;
|
||||
|
||||
public $delay = 5;
|
||||
|
||||
public function __construct()
|
||||
|
|
@ -38,8 +39,6 @@ class QuoteEmailedNotification implements ShouldQueue
|
|||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
// $first_notification_sent = true;
|
||||
|
||||
$quote = $event->invitation->quote->fresh();
|
||||
$quote->last_sent_date = now();
|
||||
$quote->saveQuietly();
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class QuoteReminderEmailActivity implements ShouldQueue
|
|||
$fields->user_id = $user_id;
|
||||
$fields->quote_id = $event->invitation->quote_id;
|
||||
$fields->company_id = $event->invitation->company_id;
|
||||
$fields->account_id = $event->invitation->company->account_id;
|
||||
$fields->client_contact_id = $event->invitation->client_contact_id;
|
||||
$fields->client_id = $event->invitation->quote->client_id;
|
||||
$fields->activity_type_id = $reminder;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,22 @@ class RegisterOrLogin extends Component
|
|||
|
||||
$this->state['initial_completed'] = true;
|
||||
|
||||
|
||||
if(!$this->subscription()->registration_required){
|
||||
|
||||
$service = new ClientRegisterService(
|
||||
company: $this->subscription()->company,
|
||||
additional: $this->additional_fields,
|
||||
);
|
||||
|
||||
$client = $service->createClient([]);
|
||||
$contact = $service->createClientContact(['email' => $this->email], $client);
|
||||
auth()->guard('contact')->loginUsingId($contact->id, true);
|
||||
$this->dispatch('purchase.next');
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if ($this->state['otp']) {
|
||||
return $this->withOtp();
|
||||
}
|
||||
|
|
@ -112,7 +128,6 @@ class RegisterOrLogin extends Component
|
|||
|
||||
if ($contact === null) {
|
||||
$this->registerForm();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -262,11 +277,10 @@ class RegisterOrLogin extends Component
|
|||
{
|
||||
|
||||
if (auth()->guard('contact')->check()) {
|
||||
// $this->dispatch('purchase.context', property: 'contact', value: auth()->guard('contact')->user());
|
||||
$this->dispatch('purchase.next');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
|
|
|||
|
|
@ -26,6 +26,16 @@ class Cart extends Component
|
|||
|
||||
public string $subscription_id;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
|
||||
\Illuminate\Support\Facades\App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(\App\Utils\Ninja::transformTranslations($this->subscription()->company->settings));
|
||||
\Illuminate\Support\Facades\App::setLocale($this->subscription()->company->locale());
|
||||
|
||||
}
|
||||
|
||||
#[Computed()]
|
||||
public function subscription()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,18 +12,20 @@
|
|||
|
||||
namespace App\Livewire\BillingPortal;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use Livewire\Component;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Livewire\BillingPortal\Authentication\Login;
|
||||
use App\Livewire\BillingPortal\Authentication\Register;
|
||||
use App\Livewire\BillingPortal\Authentication\RegisterOrLogin;
|
||||
use App\Livewire\BillingPortal\Cart\Cart;
|
||||
use App\Livewire\BillingPortal\Payments\Methods;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\On;
|
||||
use App\Models\Subscription;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Component;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Livewire\BillingPortal\Cart\Cart;
|
||||
use App\Livewire\BillingPortal\Payments\Methods;
|
||||
use App\Livewire\BillingPortal\Authentication\Login;
|
||||
use App\Livewire\BillingPortal\Authentication\Register;
|
||||
use App\Livewire\BillingPortal\Authentication\RegisterOrLogin;
|
||||
|
||||
class Purchase extends Component
|
||||
{
|
||||
|
|
@ -115,7 +117,6 @@ class Purchase extends Component
|
|||
return "summary-{$this->id}";
|
||||
}
|
||||
|
||||
|
||||
#[Computed()]
|
||||
public function subscription()
|
||||
{
|
||||
|
|
@ -166,7 +167,8 @@ class Purchase extends Component
|
|||
->handleContext('hash', $this->hash)
|
||||
->handleContext('quantity', 1)
|
||||
->handleContext('request_data', $this->request_data)
|
||||
->handleContext('campaign', $this->campaign);
|
||||
->handleContext('campaign', $this->campaign)
|
||||
->handleContext('subcription_id', $this->subscription_id);
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
|
|
|||
|
|
@ -13,12 +13,16 @@
|
|||
namespace App\Livewire\BillingPortal;
|
||||
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Subscription;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Component;
|
||||
|
||||
class RFF extends Component
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public array $context;
|
||||
|
||||
public string $contact_first_name;
|
||||
|
|
@ -70,6 +74,7 @@ class RFF extends Component
|
|||
$gateway = CompanyGateway::find($this->context['form']['company_gateway_id']);
|
||||
$countries = Cache::get('countries');
|
||||
|
||||
|
||||
if ($gateway === null) {
|
||||
return view('billing-portal.v3.rff-basic');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,15 @@
|
|||
|
||||
namespace App\Livewire\BillingPortal;
|
||||
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Subscription;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\On;
|
||||
use App\Models\Subscription;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class Summary extends Component
|
||||
{
|
||||
|
|
@ -38,6 +40,11 @@ class Summary extends Component
|
|||
{
|
||||
$subscription = Subscription::find($this->decodePrimaryKey($this->subscription_id));
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($subscription->company->settings));
|
||||
App::setLocale($subscription->company->locale());
|
||||
|
||||
$bundle = $this->context['bundle'] ?? [
|
||||
'recurring_products' => [],
|
||||
'optional_recurring_products' => [],
|
||||
|
|
@ -190,7 +197,7 @@ class Summary extends Component
|
|||
foreach ($this->context['bundle']['recurring_products'] as $key => $item) {
|
||||
$products[] = [
|
||||
'product_key' => $item['product']['product_key'],
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'])),
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'] ?? '')),
|
||||
'quantity' => $item['quantity'],
|
||||
'total_raw' => $item['product']['price'] * $item['quantity'],
|
||||
'total' => Number::formatMoney($item['product']['price'] * $item['quantity'], $this->subscription()->company) . ' / ' . RecurringInvoice::frequencyForKey($this->subscription()->frequency_id),
|
||||
|
|
@ -200,7 +207,7 @@ class Summary extends Component
|
|||
foreach ($this->context['bundle']['optional_recurring_products'] as $key => $item) {
|
||||
$products[] = [
|
||||
'product_key' => $item['product']['product_key'],
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'])),
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'] ?? '')),
|
||||
'quantity' => $item['quantity'],
|
||||
'total_raw' => $item['product']['price'] * $item['quantity'],
|
||||
'total' => Number::formatMoney($item['product']['price'] * $item['quantity'], $this->subscription()->company) . ' / ' . RecurringInvoice::frequencyForKey($this->subscription()->frequency_id),
|
||||
|
|
@ -210,7 +217,7 @@ class Summary extends Component
|
|||
foreach ($this->context['bundle']['one_time_products'] as $key => $item) {
|
||||
$products[] = [
|
||||
'product_key' => $item['product']['product_key'],
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'])),
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'] ?? '')),
|
||||
'quantity' => $item['quantity'],
|
||||
'total_raw' => $item['product']['price'] * $item['quantity'],
|
||||
'total' => Number::formatMoney($item['product']['price'] * $item['quantity'], $this->subscription()->company),
|
||||
|
|
@ -220,7 +227,7 @@ class Summary extends Component
|
|||
foreach ($this->context['bundle']['optional_one_time_products'] as $key => $item) {
|
||||
$products[] = [
|
||||
'product_key' => $item['product']['product_key'],
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'])),
|
||||
'notes' => strip_tags(\Illuminate\Support\Str::markdown($item['product']['notes'] ?? '')),
|
||||
'quantity' => $item['quantity'],
|
||||
'total_raw' => $item['product']['price'] * $item['quantity'],
|
||||
'total' => Number::formatMoney($item['product']['price'] * $item['quantity'], $this->subscription()->company),
|
||||
|
|
@ -235,17 +242,6 @@ class Summary extends Component
|
|||
#[On('summary.refresh')]
|
||||
public function refresh()
|
||||
{
|
||||
// nlog("am i refreshing here?");
|
||||
|
||||
// $this->oneTimePurchasesTotal = $this->oneTimePurchasesTotal();
|
||||
// $this->recurringPurchasesTotal = $this->recurringPurchasesTotal();
|
||||
// $this->discount = $this->discount();
|
||||
|
||||
// nlog($this->oneTimePurchasesTotal);
|
||||
// nlog($this->recurringPurchasesTotal);
|
||||
// nlog($this->discount);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,13 @@ class CreditsTable extends Component
|
|||
$query->whereDate('due_date', '>=', now())
|
||||
->orWhereNull('due_date');
|
||||
})
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
// ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->when($this->sort_field == 'number', function ($q){
|
||||
$q->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . ($this->sort_asc ? 'desc' : 'asc'));
|
||||
})
|
||||
->when($this->sort_field != 'number', function ($q){
|
||||
$q->orderBy($this->sort_field, ($this->sort_asc ? 'desc' : 'asc'));
|
||||
})
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
|
|
|||
|
|
@ -238,16 +238,18 @@ class InvoicePay extends Component
|
|||
$this->setContext('db', $this->db); // $this->context['db'] = $this->db;
|
||||
$this->setContext('invitation_id', $this->invitation_id);
|
||||
|
||||
$this->invoices = Invoice::find($this->transformKeys($this->invoices));
|
||||
$invoices = Invoice::withTrashed()
|
||||
->whereIn('id', $this->transformKeys($this->invoices))
|
||||
->where('is_deleted', 0)
|
||||
->get()
|
||||
->filter(function ($i) {
|
||||
$i = $i->service()
|
||||
->markSent()
|
||||
->removeUnpaidGatewayFees()
|
||||
->save();
|
||||
|
||||
$invoices = $this->invoices->filter(function ($i) {
|
||||
$i = $i->service()
|
||||
->markSent()
|
||||
->removeUnpaidGatewayFees()
|
||||
->save();
|
||||
|
||||
return $i->isPayable();
|
||||
});
|
||||
return $i->isPayable();
|
||||
});
|
||||
|
||||
//under-over / payment
|
||||
|
||||
|
|
@ -260,7 +262,7 @@ class InvoicePay extends Component
|
|||
$this->setContext('variables', $this->variables); // $this->context['variables'] = $this->variables;
|
||||
$this->setContext('invoices', $invoices); // $this->context['invoices'] = $invoices;
|
||||
$this->setContext('settings', $settings); // $this->context['settings'] = $settings;
|
||||
$this->setContext('invitation', $invite); // $this->context['invitation'] = $invite;
|
||||
// $this->setContext('invitation', $invite->withoutRelations()); // $this->context['invitation'] = $invite;
|
||||
|
||||
$payable_invoices = $invoices->map(function ($i) {
|
||||
/** @var \App\Models\Invoice $i */
|
||||
|
|
@ -271,7 +273,8 @@ class InvoicePay extends Component
|
|||
'formatted_currency' => Number::formatMoney($i->partial > 0 ? $i->partial : $i->balance, $i->client),
|
||||
'number' => $i->number,
|
||||
'date' => $i->translateDate($i->date, $i->client->date_format(), $i->client->locale()),
|
||||
'due_date' => $i->translateDate($i->due_date, $i->client->date_format(), $i->client->locale())
|
||||
'due_date' => $i->translateDate($i->due_date, $i->client->date_format(), $i->client->locale()),
|
||||
'terms' => $i->terms,
|
||||
];
|
||||
})->toArray();
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace App\Livewire\Flow2;
|
||||
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Utils\Number;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\On;
|
||||
|
|
@ -61,15 +62,15 @@ class InvoiceSummary extends Component
|
|||
public function downloadDocument($invoice_hashed_id)
|
||||
{
|
||||
|
||||
$contact = $this->getContext()['contact'] ?? auth()->guard('contact')->user();
|
||||
$_invoices = $this->getContext()['invoices'];
|
||||
$i = $_invoices->first(function ($i) use ($invoice_hashed_id) {
|
||||
return $i->hashed_id == $invoice_hashed_id;
|
||||
});
|
||||
$invitation_id = $this->getContext()['invitation_id'];
|
||||
|
||||
$file_name = $i->numberFormatter().'.pdf';
|
||||
$db = $this->getContext()['db'];
|
||||
|
||||
$invite = \App\Models\InvoiceInvitation::on($db)->withTrashed()->find($invitation_id);
|
||||
|
||||
$file = (new \App\Jobs\Entity\CreateRawPdf($i->invitations()->where('client_contact_id', $contact->id)->first()))->handle();
|
||||
$file_name = $invite->invoice->numberFormatter().'.pdf';
|
||||
|
||||
$file = (new \App\Jobs\Entity\CreateRawPdf($invite))->handle();
|
||||
|
||||
$headers = ['Content-Type' => 'application/pdf'];
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ class PaymentMethod extends Component
|
|||
|
||||
MultiDB::setDb($this->getContext()['db']);
|
||||
|
||||
$this->methods = $this->getContext()['invitation']->contact->client->service()->getPaymentMethods($this->amount);
|
||||
$contact = $this->getContext()['contact'] ?? auth()->guard('contact')->user();
|
||||
|
||||
$this->methods = $contact->client->service()->getPaymentMethods($this->amount);
|
||||
|
||||
if (count($this->methods) == 1) {
|
||||
$this->dispatch('singlePaymentMethodFound', company_gateway_id: $this->methods[0]['company_gateway_id'], gateway_type_id: $this->methods[0]['gateway_type_id'], amount: $this->amount);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,14 @@ class Terms extends Component
|
|||
#[Computed()]
|
||||
public function invoice()
|
||||
{
|
||||
return $this->getContext()['invoices']->first();
|
||||
|
||||
$invitation_id = $this->getContext()['invitation_id'];
|
||||
|
||||
$db = $this->getContext()['db'];
|
||||
|
||||
$invite = \App\Models\InvoiceInvitation::on($db)->withTrashed()->find($invitation_id);
|
||||
|
||||
return $invite->invoice;
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
|
|
|||
|
|
@ -32,9 +32,10 @@ class UnderOverPayment extends Component
|
|||
|
||||
public function mount()
|
||||
{
|
||||
$contact = $this->getContext()['contact'] ?? auth()->guard('contact')->user();
|
||||
|
||||
$this->invoice_amount = array_sum(array_column($this->getContext()['payable_invoices'], 'amount'));
|
||||
$this->currency = $this->getContext()['invitation']->contact->client->currency();
|
||||
$this->currency = $contact->client->currency();
|
||||
$this->payableInvoices = $this->getContext()['payable_invoices'];
|
||||
}
|
||||
|
||||
|
|
@ -44,9 +45,11 @@ class UnderOverPayment extends Component
|
|||
|
||||
$settings = $this->getContext()['settings'];
|
||||
|
||||
$contact = $this->getContext()['contact'] ?? auth()->guard('contact')->user();
|
||||
|
||||
foreach ($payableInvoices as $key => $invoice) {
|
||||
$payableInvoices[$key]['amount'] = Number::parseFloat($invoice['formatted_amount']);
|
||||
$payableInvoices[$key]['formatted_currency'] = Number::FormatMoney($payableInvoices[$key]['amount'], $this->getContext()['invitation']->contact->client);
|
||||
$payableInvoices[$key]['formatted_currency'] = Number::FormatMoney($payableInvoices[$key]['amount'], $contact->client);
|
||||
}
|
||||
|
||||
$input_amount = collect($payableInvoices)->sum('amount');
|
||||
|
|
|
|||
|
|
@ -51,7 +51,13 @@ class InvoicesTable extends Component
|
|||
->where('is_deleted', false)
|
||||
->where('is_proforma', false)
|
||||
->with('client.gateway_tokens', 'client.contacts')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
->when($this->sort_field == 'number', function ($q){
|
||||
$q->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . ($this->sort_asc ? 'desc' : 'asc'));
|
||||
})
|
||||
->when($this->sort_field != 'number', function ($q){
|
||||
$q->orderBy($this->sort_field, ($this->sort_asc ? 'desc' : 'asc'));
|
||||
});
|
||||
// ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
|
||||
if (in_array('paid', $this->status)) {
|
||||
$local_status[] = Invoice::STATUS_PAID;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,13 @@ class PaymentsTable extends Component
|
|||
->where('company_id', auth()->guard('contact')->user()->company_id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'desc' : 'asc')
|
||||
// ->orderBy($this->sort_field, $this->sort_asc ? 'desc' : 'asc')
|
||||
->when($this->sort_field == 'number', function ($q){
|
||||
$q->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . ($this->sort_asc ? 'desc' : 'asc'));
|
||||
})
|
||||
->when($this->sort_field != 'number', function ($q){
|
||||
$q->orderBy($this->sort_field, ($this->sort_asc ? 'desc' : 'asc'));
|
||||
})
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,14 @@ class QuotesTable extends Component
|
|||
{
|
||||
$query = Quote::query()
|
||||
->with('client.contacts', 'company')
|
||||
->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc');
|
||||
// ->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc');
|
||||
->when($this->sort == 'number', function ($q){
|
||||
$q->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . ($this->sort_asc ? 'desc' : 'asc'));
|
||||
})
|
||||
->when($this->sort != 'number', function ($q){
|
||||
$q->orderBy($this->sort, ($this->sort_asc ? 'desc' : 'asc'));
|
||||
});
|
||||
|
||||
|
||||
if (count($this->status) > 0) {
|
||||
/* Special filter for expired*/
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class EntitySentObject
|
|||
|
||||
private function setTemplate()
|
||||
{
|
||||
|
||||
|
||||
switch ($this->template) {
|
||||
case 'invoice':
|
||||
$this->template_subject = 'texts.notification_invoice_sent_subject';
|
||||
|
|
@ -127,6 +127,7 @@ class EntitySentObject
|
|||
$this->template_body = 'texts.notification_invoice_sent';
|
||||
break;
|
||||
case 'reminder_endless':
|
||||
case 'endless_reminder':
|
||||
$this->template_subject = 'texts.notification_invoice_reminder_endless_sent_subject';
|
||||
$this->template_body = 'texts.notification_invoice_sent';
|
||||
break;
|
||||
|
|
@ -134,6 +135,10 @@ class EntitySentObject
|
|||
$this->template_subject = 'texts.notification_quote_sent_subject';
|
||||
$this->template_body = 'texts.notification_quote_sent';
|
||||
break;
|
||||
case 'email_quote_template_reminder1':
|
||||
$this->template_subject = 'texts.notification_quote_reminder1_sent_subject';
|
||||
$this->template_body = 'texts.notification_quote_sent';
|
||||
break;
|
||||
case 'credit':
|
||||
$this->template_subject = 'texts.notification_credit_sent_subject';
|
||||
$this->template_body = 'texts.notification_credit_sent';
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ use Laracasts\Presenter\PresentableTrait;
|
|||
* @method static \Illuminate\Database\Eloquent\Builder|Account query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Account first()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Account with()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Account count()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Account where($query)
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\BankIntegration> $bank_integrations
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
|
|
@ -53,7 +54,15 @@ class Backup extends BaseModel
|
|||
{
|
||||
return $this->belongsTo(Activity::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* storeRemotely
|
||||
*
|
||||
* @param string $html
|
||||
* @param Client | Vendor $client_or_vendor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function storeRemotely(?string $html, Client | Vendor $client_or_vendor)
|
||||
{
|
||||
if (empty($html)) {
|
||||
|
|
@ -64,12 +73,40 @@ class Backup extends BaseModel
|
|||
$filename = now()->format('Y_m_d').'_'.md5(time()).'.html'; //@phpstan-ignore-line
|
||||
$file_path = $path.$filename;
|
||||
|
||||
Storage::disk(config('filesystems.default'))->put($file_path, $html);
|
||||
$disk = Ninja::isHosted() ? config('filesystems.backup') : config('filesystems.default');
|
||||
|
||||
Storage::disk($disk)->put($file_path, $html);
|
||||
|
||||
$this->filename = $file_path;
|
||||
$this->disk = $disk;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* getFile
|
||||
*
|
||||
* pulls backup file from storage
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
if(!$this->filename)
|
||||
return null;
|
||||
|
||||
$disk = Ninja::isHosted() ? $this->disk : config('filesystems.default');
|
||||
|
||||
return Storage::disk($disk)->get($this->filename);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* deleteFile
|
||||
*
|
||||
* removes backup file from storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteFile()
|
||||
{
|
||||
nlog('deleting => '.$this->filename);
|
||||
|
|
@ -78,8 +115,10 @@ class Backup extends BaseModel
|
|||
return;
|
||||
}
|
||||
|
||||
$disk = Ninja::isHosted() ? $this->disk : config('filesystems.default');
|
||||
|
||||
try {
|
||||
Storage::disk(config('filesystems.default'))->delete($this->filename);
|
||||
Storage::disk($disk)->delete($this->filename);
|
||||
} catch (\Exception $e) {
|
||||
nlog('BACKUPEXCEPTION deleting backup file with error '.$e->getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@ namespace App\Models;
|
|||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Jobs\Entity\CreateRawPdf;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
use App\Models\Traits\Excludable;
|
||||
use App\Services\EDocument\Jobes\SendEDocument;
|
||||
use App\Services\PdfMaker\PdfMerge;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use App\Services\EDocument\Jobes\SendEDocument;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
|
||||
|
||||
|
|
@ -80,6 +81,7 @@ class BaseModel extends Model
|
|||
use UserSessionAttributes;
|
||||
use HasFactory;
|
||||
use Excludable;
|
||||
use MakesDates;
|
||||
|
||||
public int $max_attachment_size = 3000000;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ use Laravel\Scout\Searchable;
|
|||
use App\DataMapper\ClientSync;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\Models\Traits\Excludable;
|
||||
use App\DataMapper\ClientSettings;
|
||||
|
|
@ -120,7 +119,6 @@ class Client extends BaseModel implements HasLocalePreference
|
|||
{
|
||||
use PresentableTrait;
|
||||
use MakesHash;
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
use GeneratesCounter;
|
||||
|
|
@ -251,6 +249,7 @@ class Client extends BaseModel implements HasLocalePreference
|
|||
}
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $name,
|
||||
'is_deleted' => $this->is_deleted,
|
||||
'hashed_id' => $this->hashed_id,
|
||||
|
|
@ -286,6 +285,11 @@ class Client extends BaseModel implements HasLocalePreference
|
|||
return $this->hashed_id;
|
||||
}
|
||||
|
||||
// public function getScoutKeyName()
|
||||
// {
|
||||
// return 'hashed_id';
|
||||
// }
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
|
@ -1029,7 +1033,35 @@ class Client extends BaseModel implements HasLocalePreference
|
|||
*/
|
||||
public function peppolSendingEnabled(): bool
|
||||
{
|
||||
return $this->getSetting('e_invoice_type') == 'PEPPOL' && $this->company->peppolSendingEnabled();
|
||||
return $this->getSetting('e_invoice_type') == 'PEPPOL' && $this->company->peppolSendingEnabled() && is_null($this->checkDeliveryNetwork());
|
||||
}
|
||||
|
||||
/**
|
||||
* checkDeliveryNetwork
|
||||
*
|
||||
* Checks whether the client country is supported
|
||||
* for sending over the PEPPOL network.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function checkDeliveryNetwork(): ?string
|
||||
{
|
||||
|
||||
if(!isset($this->country->iso_3166_2))
|
||||
return "Client has no country set!";
|
||||
|
||||
$br = new \App\DataMapper\Tax\BaseRule();
|
||||
|
||||
$government_countries = array_merge($br->peppol_business_countries, $br->peppol_government_countries);
|
||||
|
||||
if(in_array($this->country->iso_3166_2, $government_countries) && $this->classification == 'government'){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(in_array($this->country->iso_3166_2, $br->peppol_business_countries))
|
||||
return null;
|
||||
|
||||
return "Country {$this->country->full_name} ( {$this->country->iso_3166_2} ) is not supported by the PEPPOL network for e-delivery.";
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ class ClientContact extends Authenticatable implements HasLocalePreference
|
|||
public function toSearchableArray()
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->present()->search_display(),
|
||||
'hashed_id' => $this->client->hashed_id,
|
||||
'email' => $this->email,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
|
|
@ -42,7 +41,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
*/
|
||||
class ClientGatewayToken extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ class CompanyGateway extends BaseModel
|
|||
// const TYPE_AUTHORIZE = 305;
|
||||
// const TYPE_CUSTOM = 306;
|
||||
// const TYPE_BRAINTREE = 307;
|
||||
// const TYPE_WEPAY = 309;
|
||||
// const TYPE_PAYFAST = 310;
|
||||
// const TYPE_PAYTRACE = 311;
|
||||
// const TYPE_MOLLIE = 312;
|
||||
|
|
|
|||
|
|
@ -11,18 +11,20 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Utils\Number;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Models\Presenters\CreditPresenter;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use App\Services\Credit\CreditService;
|
||||
use App\Services\Ledger\LedgerService;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesInvoiceValues;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Models\Presenters\CreditPresenter;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* App\Models\Credit
|
||||
|
|
@ -128,12 +130,12 @@ class Credit extends BaseModel
|
|||
{
|
||||
use MakesHash;
|
||||
use Filterable;
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use PresentableTrait;
|
||||
use MakesInvoiceValues;
|
||||
use MakesReminders;
|
||||
|
||||
use Searchable;
|
||||
|
||||
protected $presenter = CreditPresenter::class;
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -194,6 +196,35 @@ class Credit extends BaseModel
|
|||
|
||||
public const STATUS_APPLIED = 4;
|
||||
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$locale = $this->company->locale();
|
||||
App::setLocale($locale);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => ctrans('texts.credit') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
|
||||
'hashed_id' => $this->hashed_id,
|
||||
'number' => $this->number,
|
||||
'is_deleted' => $this->is_deleted,
|
||||
'amount' => (float) $this->amount,
|
||||
'balance' => (float) $this->balance,
|
||||
'due_date' => $this->due_date,
|
||||
'date' => $this->date,
|
||||
'custom_value1' => (string)$this->custom_value1,
|
||||
'custom_value2' => (string)$this->custom_value2,
|
||||
'custom_value3' => (string)$this->custom_value3,
|
||||
'custom_value4' => (string)$this->custom_value4,
|
||||
'company_key' => $this->company->company_key,
|
||||
'po_number' => (string)$this->po_number,
|
||||
];
|
||||
}
|
||||
|
||||
public function getScoutKey()
|
||||
{
|
||||
return $this->hashed_id;
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
|
|
@ -76,7 +75,6 @@ use Illuminate\Support\Carbon;
|
|||
*/
|
||||
class CreditInvitation extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use Inviteable;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\License;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* App\Models\EInvoicingLog
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property string $tenant_id (sent|received)
|
||||
* @property string $direction
|
||||
* @property int $legal_entity_id
|
||||
* @property string|null $license_key The license key string
|
||||
* @property string|null $notes
|
||||
* @property int $counter
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property \Carbon\Carbon $deleted_at
|
||||
* @property-read \App\Models\License $license
|
||||
* @mixin \Eloquent
|
||||
*
|
||||
*/
|
||||
class EInvoicingLog extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'direction',
|
||||
'legal_entity_id',
|
||||
'license_key',
|
||||
'notes',
|
||||
'counter',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'date',
|
||||
'updated_at' => 'date',
|
||||
'deleted_at' => 'date',
|
||||
];
|
||||
|
||||
/**
|
||||
* license
|
||||
*
|
||||
*/
|
||||
public function license(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(License::class, 'license_key', 'license_key');
|
||||
}
|
||||
|
||||
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Company::class, 'tenant_id', 'id');
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +41,6 @@ class EInvoicingToken extends Model
|
|||
*/
|
||||
public function license_relation(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(License::class, 'license_key', 'license_key');
|
||||
return $this->belongsTo(License::class, 'license', 'license_key');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Utils\Number;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
|
|
@ -95,7 +98,8 @@ class Expense extends BaseModel
|
|||
{
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
|
||||
use Searchable;
|
||||
|
||||
protected $fillable = [
|
||||
'client_id',
|
||||
'assigned_user_id',
|
||||
|
|
@ -161,6 +165,33 @@ class Expense extends BaseModel
|
|||
|
||||
protected $touches = [];
|
||||
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$locale = $this->company->locale();
|
||||
|
||||
App::setLocale($locale);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => ctrans('texts.expense') . " " . $this->number . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
|
||||
'hashed_id' => $this->hashed_id,
|
||||
'number' => $this->number,
|
||||
'is_deleted' => $this->is_deleted,
|
||||
'amount' => (float) $this->amount,
|
||||
'date' => $this->date,
|
||||
'custom_value1' => (string)$this->custom_value1,
|
||||
'custom_value2' => (string)$this->custom_value2,
|
||||
'custom_value3' => (string)$this->custom_value3,
|
||||
'custom_value4' => (string)$this->custom_value4,
|
||||
'company_key' => $this->company->company_key,
|
||||
];
|
||||
}
|
||||
|
||||
public function getScoutKey()
|
||||
{
|
||||
return $this->hashed_id;
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class Gateway extends StaticModel
|
|||
} elseif ($this->id == 63) {
|
||||
$link = 'https://rotessa.com';
|
||||
} elseif ($this->id == 64) {
|
||||
$link = 'https://blockonomics.co';
|
||||
$link = 'https://help.blockonomics.co/a/solutions/articles/33000291849';
|
||||
}
|
||||
|
||||
return $link;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ use App\Utils\Ninja;
|
|||
use Laravel\Scout\Searchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\DataMapper\InvoiceSync;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
|
|
@ -143,7 +142,6 @@ class Invoice extends BaseModel
|
|||
use SoftDeletes;
|
||||
use Filterable;
|
||||
use NumberFormatter;
|
||||
use MakesDates;
|
||||
use PresentableTrait;
|
||||
use MakesInvoiceValues;
|
||||
use MakesReminders;
|
||||
|
|
@ -249,6 +247,7 @@ class Invoice extends BaseModel
|
|||
App::setLocale($locale);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => ctrans('texts.invoice') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
|
||||
'hashed_id' => $this->hashed_id,
|
||||
'number' => $this->number,
|
||||
|
|
@ -644,10 +643,10 @@ class Invoice extends BaseModel
|
|||
|
||||
public function entityEmailEvent($invitation, $reminder_template, $template = '')
|
||||
{
|
||||
|
||||
|
||||
switch ($reminder_template) {
|
||||
case 'invoice':
|
||||
event(new InvoiceWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $template));
|
||||
event(new InvoiceWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
|
||||
break;
|
||||
case 'reminder1':
|
||||
event(new InvoiceReminderWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
|
||||
|
|
@ -665,7 +664,7 @@ class Invoice extends BaseModel
|
|||
case 'custom1':
|
||||
case 'custom2':
|
||||
case 'custom3':
|
||||
event(new InvoiceWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $template));
|
||||
event(new InvoiceWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
|
|
@ -77,7 +76,6 @@ use Illuminate\Support\Carbon;
|
|||
*/
|
||||
class InvoiceInvitation extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use Inviteable;
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,16 @@ class License extends StaticModel
|
|||
|
||||
}
|
||||
|
||||
public function countEntities(): int
|
||||
{
|
||||
|
||||
if (!is_array($this->entities)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count($this->entities);
|
||||
}
|
||||
|
||||
public function findEntity(string $key, mixed $value): ?TaxEntity
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use App\Services\Payment\PaymentService;
|
|||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Payment\Refundable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
|
@ -97,7 +96,6 @@ class Payment extends BaseModel
|
|||
{
|
||||
use MakesHash;
|
||||
use Filterable;
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use Refundable;
|
||||
use Inviteable;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class UserPresenter extends EntityPresenter
|
|||
return 'No First Name Available';
|
||||
}
|
||||
|
||||
return $this->entity->first_name ?? 'First Name';
|
||||
return $this->entity->first_name ?? ' ';
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,4 +39,9 @@ class VendorContactPresenter extends EntityPresenter
|
|||
{
|
||||
return $this->entity->last_name ?: '';
|
||||
}
|
||||
|
||||
public function search_display()
|
||||
{
|
||||
return strlen($this->entity->email ?? '') > 2 ? $this->name().' <'.$this->entity->email.'>' : $this->name();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Services\Project\ProjectService;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
|
@ -143,6 +144,15 @@ class Project extends BaseModel
|
|||
return $this->hasMany(Quote::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service entry points.
|
||||
*
|
||||
* @return ProjectService
|
||||
*/
|
||||
public function service(): ProjectService
|
||||
{
|
||||
return new ProjectService($this);
|
||||
}
|
||||
|
||||
public function translate_entity()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Services\PurchaseOrder\PurchaseOrderService;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Utils\Number;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Services\PurchaseOrder\PurchaseOrderService;
|
||||
|
||||
/**
|
||||
* App\Models\PurchaseOrder
|
||||
|
|
@ -117,8 +119,8 @@ class PurchaseOrder extends BaseModel
|
|||
{
|
||||
use Filterable;
|
||||
use SoftDeletes;
|
||||
use MakesDates;
|
||||
|
||||
use Searchable;
|
||||
|
||||
protected $hidden = [
|
||||
'id',
|
||||
'private_notes',
|
||||
|
|
@ -197,6 +199,36 @@ class PurchaseOrder extends BaseModel
|
|||
public const STATUS_RECEIVED = 4;
|
||||
public const STATUS_CANCELLED = 5;
|
||||
|
||||
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$locale = $this->company->locale();
|
||||
App::setLocale($locale);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => ctrans('texts.purchase_order') . " " . $this->number . " | " . $this->vendor->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
|
||||
'hashed_id' => $this->hashed_id,
|
||||
'number' => $this->number,
|
||||
'is_deleted' => $this->is_deleted,
|
||||
'amount' => (float) $this->amount,
|
||||
'balance' => (float) $this->balance,
|
||||
'due_date' => $this->due_date,
|
||||
'date' => $this->date,
|
||||
'custom_value1' => (string)$this->custom_value1,
|
||||
'custom_value2' => (string)$this->custom_value2,
|
||||
'custom_value3' => (string)$this->custom_value3,
|
||||
'custom_value4' => (string)$this->custom_value4,
|
||||
'company_key' => $this->company->company_key,
|
||||
'po_number' => (string)$this->po_number,
|
||||
];
|
||||
}
|
||||
|
||||
public function getScoutKey()
|
||||
{
|
||||
return $this->hashed_id;
|
||||
}
|
||||
|
||||
public static function stringStatus(int $status)
|
||||
{
|
||||
switch ($status) {
|
||||
|
|
@ -214,7 +246,6 @@ class PurchaseOrder extends BaseModel
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static function badgeForStatus(int $status)
|
||||
{
|
||||
switch ($status) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ namespace App\Models;
|
|||
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
@ -76,7 +75,6 @@ use Illuminate\Support\Str;
|
|||
*/
|
||||
class PurchaseOrderInvitation extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use Inviteable;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,17 +11,22 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Models\Presenters\QuotePresenter;
|
||||
use App\Services\Quote\QuoteService;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesInvoiceValues;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Services\Quote\QuoteService;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use App\Events\Quote\QuoteWasEmailed;
|
||||
use App\Utils\Traits\MakesInvoiceValues;
|
||||
use App\Models\Presenters\QuotePresenter;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Events\Quote\QuoteReminderWasEmailed;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* App\Models\Quote
|
||||
|
|
@ -113,12 +118,12 @@ use Laracasts\Presenter\PresentableTrait;
|
|||
class Quote extends BaseModel
|
||||
{
|
||||
use MakesHash;
|
||||
use MakesDates;
|
||||
use Filterable;
|
||||
use SoftDeletes;
|
||||
use MakesReminders;
|
||||
use PresentableTrait;
|
||||
use MakesInvoiceValues;
|
||||
use Searchable;
|
||||
|
||||
protected $presenter = QuotePresenter::class;
|
||||
|
||||
|
|
@ -187,6 +192,35 @@ class Quote extends BaseModel
|
|||
|
||||
public const STATUS_EXPIRED = -1;
|
||||
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$locale = $this->company->locale();
|
||||
App::setLocale($locale);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => ctrans('texts.quote') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
|
||||
'hashed_id' => $this->hashed_id,
|
||||
'number' => $this->number,
|
||||
'is_deleted' => $this->is_deleted,
|
||||
'amount' => (float) $this->amount,
|
||||
'balance' => (float) $this->balance,
|
||||
'due_date' => $this->due_date,
|
||||
'date' => $this->date,
|
||||
'custom_value1' => (string)$this->custom_value1,
|
||||
'custom_value2' => (string)$this->custom_value2,
|
||||
'custom_value3' => (string)$this->custom_value3,
|
||||
'custom_value4' => (string)$this->custom_value4,
|
||||
'company_key' => $this->company->company_key,
|
||||
'po_number' => (string)$this->po_number,
|
||||
];
|
||||
}
|
||||
|
||||
public function getScoutKey()
|
||||
{
|
||||
return $this->hashed_id;
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
|
@ -197,16 +231,6 @@ class Quote extends BaseModel
|
|||
return $this->dateMutator($value);
|
||||
}
|
||||
|
||||
// public function getDueDateAttribute($value)
|
||||
// {
|
||||
// return $value ? $this->dateMutator($value) : null;
|
||||
// }
|
||||
|
||||
// public function getPartialDueDateAttribute($value)
|
||||
// {
|
||||
// return $this->dateMutator($value);
|
||||
// }
|
||||
|
||||
public function getStatusIdAttribute($value)
|
||||
{
|
||||
if ($this->due_date && ! $this->is_deleted && $value == self::STATUS_SENT && Carbon::parse($this->due_date)->lte(now()->startOfDay())) {
|
||||
|
|
@ -442,5 +466,34 @@ class Quote extends BaseModel
|
|||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* entityEmailEvent
|
||||
*
|
||||
* Translates the email type into an activity + notification
|
||||
* that matches.
|
||||
*/
|
||||
public function entityEmailEvent($invitation, $reminder_template)
|
||||
{
|
||||
|
||||
switch ($reminder_template) {
|
||||
case 'quote':
|
||||
event(new QuoteWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
|
||||
break;
|
||||
case 'email_quote_template_reminder1':
|
||||
event(new QuoteReminderWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
|
||||
break;
|
||||
case 'custom1':
|
||||
case 'custom2':
|
||||
case 'custom3':
|
||||
event(new QuoteWasEmailed($invitation, $invitation->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
|
|
@ -57,7 +56,6 @@ use Illuminate\Support\Carbon;
|
|||
*/
|
||||
class QuoteInvitation extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use Inviteable;
|
||||
use SoftDeletes;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,18 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Models\Presenters\RecurringInvoicePresenter;
|
||||
use App\Services\Recurring\RecurringService;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Number;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Services\Recurring\RecurringService;
|
||||
use App\Utils\Traits\Recurring\HasRecurrence;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Models\Presenters\RecurringInvoicePresenter;
|
||||
|
||||
/**
|
||||
* Class for Recurring Invoices.
|
||||
|
|
@ -129,10 +131,10 @@ class RecurringInvoice extends BaseModel
|
|||
use MakesHash;
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
use MakesDates;
|
||||
use HasRecurrence;
|
||||
use PresentableTrait;
|
||||
|
||||
use Searchable;
|
||||
|
||||
protected $presenter = RecurringInvoicePresenter::class;
|
||||
|
||||
/**
|
||||
|
|
@ -261,6 +263,35 @@ class RecurringInvoice extends BaseModel
|
|||
'footer',
|
||||
];
|
||||
|
||||
public function toSearchableArray()
|
||||
{
|
||||
$locale = $this->company->locale();
|
||||
App::setLocale($locale);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => ctrans('texts.recurring_invoice') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
|
||||
'hashed_id' => $this->hashed_id,
|
||||
'number' => $this->number,
|
||||
'is_deleted' => $this->is_deleted,
|
||||
'amount' => (float) $this->amount,
|
||||
'balance' => (float) $this->balance,
|
||||
'due_date' => $this->due_date,
|
||||
'date' => $this->date,
|
||||
'custom_value1' => (string)$this->custom_value1,
|
||||
'custom_value2' => (string)$this->custom_value2,
|
||||
'custom_value3' => (string)$this->custom_value3,
|
||||
'custom_value4' => (string)$this->custom_value4,
|
||||
'company_key' => $this->company->company_key,
|
||||
'po_number' => (string)$this->po_number,
|
||||
];
|
||||
}
|
||||
|
||||
public function getScoutKey()
|
||||
{
|
||||
return $this->hashed_id;
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
|
@ -508,6 +539,40 @@ class RecurringInvoice extends BaseModel
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public function nextDateByFrequencyNoOffset(Carbon $carbon)
|
||||
{
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case self::FREQUENCY_DAILY:
|
||||
return $carbon->startOfDay()->addDay();
|
||||
case self::FREQUENCY_WEEKLY:
|
||||
return $carbon->startOfDay()->addWeek();
|
||||
case self::FREQUENCY_TWO_WEEKS:
|
||||
return $carbon->startOfDay()->addWeeks(2);
|
||||
case self::FREQUENCY_FOUR_WEEKS:
|
||||
return $carbon->startOfDay()->addWeeks(4);
|
||||
case self::FREQUENCY_MONTHLY:
|
||||
return $carbon->startOfDay()->addMonthNoOverflow();
|
||||
case self::FREQUENCY_TWO_MONTHS:
|
||||
return $carbon->startOfDay()->addMonthsNoOverflow(2);
|
||||
case self::FREQUENCY_THREE_MONTHS:
|
||||
return $carbon->startOfDay()->addMonthsNoOverflow(3);
|
||||
case self::FREQUENCY_FOUR_MONTHS:
|
||||
return $carbon->startOfDay()->addMonthsNoOverflow(4);
|
||||
case self::FREQUENCY_SIX_MONTHS:
|
||||
return $carbon->addMonthsNoOverflow(6);
|
||||
case self::FREQUENCY_ANNUALLY:
|
||||
return $carbon->startOfDay()->addYear();
|
||||
case self::FREQUENCY_TWO_YEARS:
|
||||
return $carbon->startOfDay()->addYears(2);
|
||||
case self::FREQUENCY_THREE_YEARS:
|
||||
return $carbon->startOfDay()->addYears(3);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function remainingCycles(): int
|
||||
{
|
||||
if ($this->remaining_cycles == 0) {
|
||||
|
|
@ -642,26 +707,26 @@ class RecurringInvoice extends BaseModel
|
|||
return $data;
|
||||
}
|
||||
|
||||
$next_send_date = Carbon::parse($this->next_send_date_client)->copy();
|
||||
$next_send_date = Carbon::parse($this->next_send_date_client)->startOfDay()->copy();
|
||||
|
||||
for ($x = 0; $x < $iterations; $x++) {
|
||||
// we don't add the days... we calc the day of the month!!
|
||||
$next_due_date = $this->calculateDueDate($next_send_date->copy()->format('Y-m-d'));
|
||||
$next_due_date_string = $next_due_date ? $next_due_date->format('Y-m-d') : '';
|
||||
|
||||
$next_send_date = Carbon::parse($next_send_date);
|
||||
// $next_send_date = Carbon::parse($next_send_date);
|
||||
|
||||
$data[] = [
|
||||
'send_date' => $next_send_date->format('Y-m-d'),
|
||||
'due_date' => $next_due_date_string,
|
||||
];
|
||||
|
||||
/* Fixes the timeshift in case the offset is negative which cause a infinite loop due to UTC +0*/
|
||||
if ($this->client->timezone_offset() < 0) {
|
||||
$next_send_date = $this->nextDateByFrequency($next_send_date->addDay()->format('Y-m-d'));
|
||||
} else {
|
||||
$next_send_date = $this->nextDateByFrequency($next_send_date->format('Y-m-d'));
|
||||
}
|
||||
// /* Fixes the timeshift in case the offset is negative which cause a infinite loop due to UTC +0*/
|
||||
// if ($this->client->timezone_offset() < 0) {
|
||||
// $next_send_date = $this->nextSendDateClient($next_send_date->addDay()->format('Y-m-d'));
|
||||
// } else {
|
||||
$next_send_date = $this->nextDateByFrequencyNoOffset($next_send_date);
|
||||
// }
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue