mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' into v5-stable
This commit is contained in:
commit
6370b1f061
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -46,7 +46,9 @@ jobs:
|
||||
git checkout main
|
||||
npm i
|
||||
npm run build
|
||||
cp -r dist/react/* ../public/react
|
||||
cp -r dist/react/* ../public/react
|
||||
mkdir -p ../public/tinymce_6.4.2/tinymce/js/
|
||||
cp -r node_modules/tinymce ../public/tinymce_6.4.2/tinymce/js/
|
||||
cd ..
|
||||
rm -rf ui
|
||||
php artisan ninja:react
|
||||
|
@ -1 +1 @@
|
||||
5.6.4
|
||||
5.6.10
|
@ -154,7 +154,7 @@ class CheckData extends Command
|
||||
->subject('Check-Data: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)." [{$database}]");
|
||||
});
|
||||
} elseif (! $this->isValid) {
|
||||
new Exception("Check data failed!!\n".$this->log);
|
||||
new \Exception("Check data failed!!".$this->log);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class ImportMigrations extends Command
|
||||
$import_file = public_path("storage/migrations/$filename/migration.json");
|
||||
|
||||
Import::dispatch($import_file, $this->getUser()->companies()->first(), $this->getUser());
|
||||
// StartMigration::dispatch($file->getRealPath(), $this->getUser(), $this->getUser()->companies()->first());
|
||||
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
|
||||
\Mail::to($user)->send(new MigrationFailed($e, $company));
|
||||
|
||||
|
@ -133,11 +133,9 @@ class TranslationsExport extends Command
|
||||
Storage::disk('local')->makeDirectory('lang');
|
||||
|
||||
foreach ($this->langs as $lang) {
|
||||
nlog($lang);
|
||||
Storage::disk('local')->makeDirectory("lang/{$lang}");
|
||||
|
||||
$translations = Lang::getLoader()->load($lang, 'texts');
|
||||
nlog($translations);
|
||||
Storage::disk('local')->put("lang/{$lang}/{$lang}.json", json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ class BaseRule implements RuleInterface
|
||||
$this->client_region = $this->region_codes[$this->client->country->iso_3166_2];
|
||||
|
||||
match($this->client_region){
|
||||
'US' => $this->client_subregion = strlen($this->invoice?->client?->tax_data?->geoState) > 1 ? $this->invoice->client->tax_data->geoState : $this->getUSState(),
|
||||
'US' => $this->client_subregion = isset($this->invoice?->client?->tax_data?->geoState) ? $this->invoice->client->tax_data->geoState : $this->getUSState(),
|
||||
'EU' => $this->client_subregion = $this->client->country->iso_3166_2,
|
||||
'AU' => $this->client_subregion = 'AU',
|
||||
default => $this->client_subregion = $this->client->country->iso_3166_2,
|
||||
|
@ -48,7 +48,6 @@ class Rule extends BaseRule implements RuleInterface
|
||||
{
|
||||
|
||||
$this->tax_rate1 = $item->tax_rate1;
|
||||
|
||||
$this->tax_name1 = $item->tax_name1;
|
||||
|
||||
return $this;
|
||||
@ -117,6 +116,9 @@ class Rule extends BaseRule implements RuleInterface
|
||||
if(in_array($this->tax_data?->txbService,['Y','L'])) {
|
||||
$this->default($item);
|
||||
}
|
||||
else {
|
||||
$this->taxExempt($item);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -162,14 +164,13 @@ class Rule extends BaseRule implements RuleInterface
|
||||
|
||||
if($this->tax_data?->stateSalesTax == 0) {
|
||||
|
||||
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
|
||||
$this->tax_name1 = "Sales Tax";
|
||||
$this->tax_rate1 = 0;
|
||||
$this->tax_name1 = '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->tax_rate1 = $this->tax_data->taxSales * 100;
|
||||
// $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
|
||||
$this->tax_name1 = "Sales Tax";
|
||||
|
||||
return $this;
|
||||
|
@ -13,9 +13,10 @@ namespace App\Events\Payment;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use App\Models\ClientContact;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
|
||||
/**
|
||||
* Class PaymentWasEmailed.
|
||||
@ -24,26 +25,15 @@ class PaymentWasEmailed
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Payment
|
||||
*/
|
||||
public $payment;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Payment $payment
|
||||
* @param Company $company
|
||||
* @param ClientContact $contact
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(Payment $payment, Company $company, array $event_vars)
|
||||
public function __construct(public Payment $payment, public Company $company, public ClientContact $contact, public array $event_vars)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ class Handler extends ExceptionHandler
|
||||
|
||||
if($exception instanceof ThrottleRequestsException && class_exists(\Modules\Admin\Events\ThrottledExceptionRaised::class)) {
|
||||
$uri = urldecode(request()->getRequestUri());
|
||||
event(new \Modules\Admin\Events\ThrottledExceptionRaised(auth()->user()?->account?->key, $uri, request()->ip()));
|
||||
// event(new \Modules\Admin\Events\ThrottledExceptionRaised(auth()->user()?->account?->key, $uri, request()->ip()));
|
||||
}
|
||||
|
||||
Integration::configureScope(function (Scope $scope): void {
|
||||
|
41
app/Exceptions/QuoteConversion.php
Normal file
41
app/Exceptions/QuoteConversion.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class QuoteConversion extends Exception
|
||||
{
|
||||
/**
|
||||
* Report the exception.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function report()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function render($request)
|
||||
{
|
||||
return response()->json(['message' => 'This quote has already been converted. Cannot convert again.'], 400);
|
||||
}
|
||||
}
|
@ -11,11 +11,18 @@
|
||||
|
||||
namespace App\Export\CSV;
|
||||
|
||||
use App\Utils\Number;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use League\Fractal\Manager;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Transformers\ClientTransformer;
|
||||
use App\Transformers\PaymentTransformer;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
|
||||
class BaseExport
|
||||
{
|
||||
@ -35,6 +42,161 @@ class BaseExport
|
||||
|
||||
public array $forced_keys = [];
|
||||
|
||||
protected array $client_report_keys = [
|
||||
"name" => "client.name",
|
||||
"user" => "client.user_id",
|
||||
"balance" => "client.balance",
|
||||
"paid_to_date" => "client.paid_to_date",
|
||||
"currency" => "client.currency_id",
|
||||
"website" => "client.website",
|
||||
"private_notes" => "client.private_notes",
|
||||
"industry" => "client.industry_id",
|
||||
"size" => "client.size_id",
|
||||
"work_phone" => "client.phone",
|
||||
"address1" => "client.address1",
|
||||
"address2" => "client.address2",
|
||||
"city" => "client.city",
|
||||
"state" => "client.state",
|
||||
"postal_code" => "client.postal_code",
|
||||
"country" => "client.country_id",
|
||||
"custom_value4" => "contact.custom_value4",
|
||||
"shipping_address1" => "client.shipping_address1",
|
||||
"shipping_address2" => "client.shipping_address2",
|
||||
"shipping_city" => "client.shipping_city",
|
||||
"shipping_state" => "client.shipping_state",
|
||||
"shipping_postal_code" => "client.shipping_postal_code",
|
||||
"shipping_country" => "client.shipping_country_id",
|
||||
"payment_terms" => "client.payment_terms",
|
||||
"vat_number" => "client.vat_number",
|
||||
"id_number" => "client.id_number",
|
||||
"public_notes" => "client.public_notes",
|
||||
"phone" => "contact.phone",
|
||||
"first_name" => "contact.first_name",
|
||||
"last_name" => "contact.last_name",
|
||||
"email" => "contact.email",
|
||||
];
|
||||
|
||||
protected array $invoice_report_keys = [
|
||||
"invoice_number" => "invoice.number",
|
||||
"amount" => "invoice.amount",
|
||||
"balance" => "invoice.balance",
|
||||
"paid_to_date" => "invoice.paid_to_date",
|
||||
"po_number" => "invoice.po_number",
|
||||
"date" => "invoice.date",
|
||||
"due_date" => "invoice.due_date",
|
||||
"terms" => "invoice.terms",
|
||||
"footer" => "invoice.footer",
|
||||
"status" => "invoice.status",
|
||||
"public_notes" => "invoice.public_notes",
|
||||
"private_notes" => "invoice.private_notes",
|
||||
"uses_inclusive_taxes" => "invoice.uses_inclusive_taxes",
|
||||
"is_amount_discount" => "invoice.is_amount_discount",
|
||||
"partial" => "invoice.partial",
|
||||
"partial_due_date" => "invoice.partial_due_date",
|
||||
"surcharge1" => "invoice.custom_surcharge1",
|
||||
"surcharge2" => "invoice.custom_surcharge2",
|
||||
"surcharge3" => "invoice.custom_surcharge3",
|
||||
"surcharge4" => "invoice.custom_surcharge4",
|
||||
"exchange_rate" => "invoice.exchange_rate",
|
||||
"tax_amount" => "invoice.total_taxes",
|
||||
"assigned_user" => "invoice.assigned_user_id",
|
||||
"user" => "invoice.user_id",
|
||||
];
|
||||
|
||||
protected array $item_report_keys = [
|
||||
"quantity" => "item.quantity",
|
||||
"cost" => "item.cost",
|
||||
"product_key" => "item.product_key",
|
||||
"notes" => "item.notes",
|
||||
"item_tax1" => "item.tax_name1",
|
||||
"item_tax_rate1" => "item.tax_rate1",
|
||||
"item_tax2" => "item.tax_name2",
|
||||
"item_tax_rate2" => "item.tax_rate2",
|
||||
"item_tax3" => "item.tax_name3",
|
||||
"item_tax_rate3" => "item.tax_rate3",
|
||||
"custom_value1" => "item.custom_value1",
|
||||
"custom_value2" => "item.custom_value2",
|
||||
"custom_value3" => "item.custom_value3",
|
||||
"custom_value4" => "item.custom_value4",
|
||||
"discount" => "item.discount",
|
||||
"type" => "item.type_id",
|
||||
"tax_category" => "item.tax_id",
|
||||
];
|
||||
|
||||
protected array $quote_report_keys = [
|
||||
"quote_number" => "quote.number",
|
||||
"amount" => "quote.amount",
|
||||
"balance" => "quote.balance",
|
||||
"paid_to_date" => "quote.paid_to_date",
|
||||
"po_number" => "quote.po_number",
|
||||
"date" => "quote.date",
|
||||
"due_date" => "quote.due_date",
|
||||
"terms" => "quote.terms",
|
||||
"footer" => "quote.footer",
|
||||
"status" => "quote.status",
|
||||
"public_notes" => "quote.public_notes",
|
||||
"private_notes" => "quote.private_notes",
|
||||
"uses_inclusive_taxes" => "quote.uses_inclusive_taxes",
|
||||
"is_amount_discount" => "quote.is_amount_discount",
|
||||
"partial" => "quote.partial",
|
||||
"partial_due_date" => "quote.partial_due_date",
|
||||
"surcharge1" => "quote.custom_surcharge1",
|
||||
"surcharge2" => "quote.custom_surcharge2",
|
||||
"surcharge3" => "quote.custom_surcharge3",
|
||||
"surcharge4" => "quote.custom_surcharge4",
|
||||
"exchange_rate" => "quote.exchange_rate",
|
||||
"tax_amount" => "quote.total_taxes",
|
||||
"assigned_user" => "quote.assigned_user_id",
|
||||
"user" => "quote.user_id",
|
||||
];
|
||||
|
||||
protected array $credit_report_keys = [
|
||||
"credit_number" => "credit.number",
|
||||
"amount" => "credit.amount",
|
||||
"balance" => "credit.balance",
|
||||
"paid_to_date" => "credit.paid_to_date",
|
||||
"po_number" => "credit.po_number",
|
||||
"date" => "credit.date",
|
||||
"due_date" => "credit.due_date",
|
||||
"terms" => "credit.terms",
|
||||
"footer" => "credit.footer",
|
||||
"status" => "credit.status",
|
||||
"public_notes" => "credit.public_notes",
|
||||
"private_notes" => "credit.private_notes",
|
||||
"uses_inclusive_taxes" => "credit.uses_inclusive_taxes",
|
||||
"is_amount_discount" => "credit.is_amount_discount",
|
||||
"partial" => "credit.partial",
|
||||
"partial_due_date" => "credit.partial_due_date",
|
||||
"surcharge1" => "credit.custom_surcharge1",
|
||||
"surcharge2" => "credit.custom_surcharge2",
|
||||
"surcharge3" => "credit.custom_surcharge3",
|
||||
"surcharge4" => "credit.custom_surcharge4",
|
||||
"exchange_rate" => "credit.exchange_rate",
|
||||
"tax_amount" => "credit.total_taxes",
|
||||
"assigned_user" => "credit.assigned_user_id",
|
||||
"user" => "credit.user_id",
|
||||
];
|
||||
|
||||
protected array $payment_report_keys = [
|
||||
"date" => "payment.date",
|
||||
"amount" => "payment.amount",
|
||||
"refunded" => "payment.refunded",
|
||||
"applied" => "payment.applied",
|
||||
"transaction_reference" => "payment.transaction_reference",
|
||||
"currency" => "payment.currency",
|
||||
"exchange_rate" => "payment.exchange_rate",
|
||||
"number" => "payment.number",
|
||||
"method" => "payment.method",
|
||||
"status" => "payment.status",
|
||||
"private_notes" => "payment.private_notes",
|
||||
"custom_value1" => "payment.custom_value1",
|
||||
"custom_value2" => "payment.custom_value2",
|
||||
"custom_value3" => "payment.custom_value3",
|
||||
"custom_value4" => "payment.custom_value4",
|
||||
"user" => "payment.user_id",
|
||||
"assigned_user" => "payment.assigned_user_id",
|
||||
];
|
||||
|
||||
protected function filterByClients($query)
|
||||
{
|
||||
if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') {
|
||||
@ -50,6 +212,167 @@ class BaseExport
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function resolveKey($key, $entity, $transformer) :string
|
||||
{
|
||||
$parts = explode(".", $key);
|
||||
|
||||
if(!is_array($parts) || count($parts) < 2)
|
||||
return '';
|
||||
|
||||
match($parts[0]) {
|
||||
'contact' => $value = $this->resolveClientContactKey($parts[1], $entity, $transformer),
|
||||
'client' => $value = $this->resolveClientKey($parts[1], $entity, $transformer),
|
||||
'invoice' => $value = $this->resolveInvoiceKey($parts[1], $entity, $transformer),
|
||||
'payment' => $value = $this->resolvePaymentKey($parts[1], $entity, $transformer),
|
||||
default => $value = ''
|
||||
};
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function resolveClientContactKey($column, $entity, $transformer)
|
||||
{
|
||||
|
||||
$primary_contact = $entity->client->primary_contact()->first() ?? $entity->client->contacts()->first();
|
||||
|
||||
return $primary_contact?->{$column} ?? '';
|
||||
|
||||
}
|
||||
|
||||
private function resolveClientKey($column, $entity, $transformer)
|
||||
{
|
||||
$transformed_client = $transformer->includeClient($entity);
|
||||
|
||||
$manager = new Manager();
|
||||
$manager->setSerializer(new ArraySerializer());
|
||||
$transformed_client = $manager->createData($transformed_client)->toArray();
|
||||
|
||||
if($column == 'name')
|
||||
return $transformed_client['display_name'];
|
||||
|
||||
if($column == 'user_id')
|
||||
return $entity->client->user->present()->name();
|
||||
|
||||
if($column == 'country_id')
|
||||
return $entity->client->country ? ctrans("texts.country_{$entity->client->country->name}") : '';
|
||||
|
||||
if($column == 'shipping_country_id')
|
||||
return $entity->client->shipping_country ? ctrans("texts.country_{$entity->client->shipping_country->name}") : '';
|
||||
|
||||
if($column == 'size_id')
|
||||
return $entity->client->size?->name ?? '';
|
||||
|
||||
if($column == 'industry_id')
|
||||
return $entity->client->industry?->name ?? '';
|
||||
|
||||
if ($column == 'currency_id') {
|
||||
return $entity->client->currency() ? $entity->client->currency()->code : $entity->company->currency()->code;
|
||||
}
|
||||
|
||||
if($column == 'client.payment_terms') {
|
||||
return $entity->client->getSetting('payment_terms');
|
||||
}
|
||||
|
||||
if(array_key_exists($column, $transformed_client))
|
||||
return $transformed_client[$column];
|
||||
|
||||
nlog("export: Could not resolve client key: {$column}");
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
private function resolveInvoiceKey($column, $entity, $transformer)
|
||||
{
|
||||
nlog("searching for {$column}");
|
||||
|
||||
if($transformer instanceof PaymentTransformer) {
|
||||
$transformed_invoices = $transformer->includeInvoices($entity);
|
||||
|
||||
$manager = new Manager();
|
||||
$manager->setSerializer(new ArraySerializer());
|
||||
$transformed_invoices = $manager->createData($transformed_invoices)->toArray();
|
||||
|
||||
if(!isset($transformed_invoices['App\\Models\\Invoice']))
|
||||
return '';
|
||||
|
||||
$transformed_invoices = $transformed_invoices['App\\Models\\Invoice'];
|
||||
|
||||
if(count($transformed_invoices) == 1 && array_key_exists($column, $transformed_invoices[0]))
|
||||
return $transformed_invoices[0][$column];
|
||||
|
||||
if(count($transformed_invoices) > 1 && array_key_exists($column, $transformed_invoices[0]))
|
||||
return implode(', ', array_column($transformed_invoices, $column));
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
$transformed_invoice = $transformer->transform($entity);
|
||||
|
||||
if($column == 'status')
|
||||
return $entity->stringStatus($entity->status_id);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function resolvePaymentKey($column, $entity, $transformer)
|
||||
{
|
||||
if($entity instanceof Payment){
|
||||
|
||||
$transformed_payment = $transformer->transform($entity);
|
||||
|
||||
if(array_key_exists($column, $transformed_payment)) {
|
||||
return $transformed_payment[$column];
|
||||
} elseif (array_key_exists(str_replace("payment.", "", $column), $transformed_payment)) {
|
||||
return $transformed_payment[$column];
|
||||
}
|
||||
|
||||
nlog("export: Could not resolve payment key: {$column}");
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
if($column == 'amount')
|
||||
return $entity->payments()->exists() ? Number::formatMoney($entity->payments()->sum('paymentables.amount'), $entity->company) : ctrans('texts.unpaid');
|
||||
|
||||
if($column == 'refunded') {
|
||||
return $entity->payments()->exists() ? Number::formatMoney($entity->payments()->sum('paymentables.refunded'), $entity->company) : 0;
|
||||
}
|
||||
|
||||
if($column == 'applied') {
|
||||
$refunded = $entity->payments()->sum('paymentables.refunded');
|
||||
$amount = $entity->payments()->sum('paymentables.amount');
|
||||
|
||||
return $entity->payments()->exists() ? Number::formatMoney(($amount - $refunded), $entity->company) : 0;
|
||||
}
|
||||
|
||||
$payment = $entity->payments()->first();
|
||||
|
||||
if(!$payment)
|
||||
return '';
|
||||
|
||||
if($column == 'method')
|
||||
return $payment->translatedType();
|
||||
|
||||
if($column == 'currency')
|
||||
return $payment?->currency?->code ?? '';
|
||||
|
||||
$payment_transformer = new PaymentTransformer();
|
||||
$transformed_payment = $payment_transformer->transform($payment);
|
||||
|
||||
if($column == 'status'){
|
||||
return $payment->stringStatus($transformed_payment['status_id']);
|
||||
}
|
||||
|
||||
if(array_key_exists($column, $transformed_payment))
|
||||
return $transformed_payment[$column];
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
protected function addInvoiceStatusFilter($query, $status): Builder
|
||||
{
|
||||
|
||||
@ -173,14 +496,53 @@ class BaseExport
|
||||
$header = [];
|
||||
|
||||
foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) {
|
||||
|
||||
$key = array_search($value, $this->entity_keys);
|
||||
|
||||
$prefix = '';
|
||||
|
||||
if(!$key) {
|
||||
$prefix = stripos($value, 'client.') !== false ? ctrans('texts.client') : ctrans('texts.contact');
|
||||
$key = array_search($value, $this->client_report_keys);
|
||||
}
|
||||
|
||||
if(!$key) {
|
||||
$prefix = ctrans('texts.invoice');
|
||||
$key = array_search($value, $this->invoice_report_keys);
|
||||
}
|
||||
|
||||
if(!$key) {
|
||||
$prefix = ctrans('texts.payment');
|
||||
$key = array_search($value, $this->payment_report_keys);
|
||||
}
|
||||
|
||||
|
||||
if(!$key) {
|
||||
$prefix = ctrans('texts.quote');
|
||||
$key = array_search($value, $this->quote_report_keys);
|
||||
}
|
||||
|
||||
if(!$key) {
|
||||
$prefix = ctrans('texts.credit');
|
||||
$key = array_search($value, $this->credit_report_keys);
|
||||
}
|
||||
|
||||
if(!$key) {
|
||||
$prefix = ctrans('texts.item');
|
||||
$key = array_search($value, $this->item_report_keys);
|
||||
}
|
||||
|
||||
if(!$key) {
|
||||
$prefix = '';
|
||||
}
|
||||
|
||||
$key = str_replace('item.', '', $key);
|
||||
$key = str_replace('invoice.', '', $key);
|
||||
$key = str_replace('client.', '', $key);
|
||||
$key = str_replace('contact.', '', $key);
|
||||
$key = str_replace('payment.', '', $key);
|
||||
|
||||
$header[] = ctrans("texts.{$key}");
|
||||
$header[] = "{$prefix} " . ctrans("texts.{$key}");
|
||||
}
|
||||
|
||||
return $header;
|
||||
|
@ -143,9 +143,9 @@ class ClientExport extends BaseExport
|
||||
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
|
||||
if ($parts[0] == 'client' && array_key_exists($parts[1], $transformed_client)) {
|
||||
if (is_array($parts) && $parts[0] == 'client' && array_key_exists($parts[1], $transformed_client)) {
|
||||
$entity[$keyval] = $transformed_client[$parts[1]];
|
||||
} elseif ($parts[0] == 'contact' && array_key_exists($parts[1], $transformed_contact)) {
|
||||
} elseif (is_array($parts) && $parts[0] == 'contact' && array_key_exists($parts[1], $transformed_contact)) {
|
||||
$entity[$keyval] = $transformed_contact[$parts[1]];
|
||||
} else {
|
||||
$entity[$keyval] = '';
|
||||
|
@ -123,10 +123,19 @@ class CreditExport extends BaseExport
|
||||
foreach (array_values($this->input['report_keys']) as $key) {
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
|
||||
if(!$keyval)
|
||||
$keyval = array_search(str_replace("credit.", "", $key), $this->entity_keys) ?? $key;
|
||||
|
||||
if(!$keyval)
|
||||
$keyval = $key;
|
||||
|
||||
if (array_key_exists($key, $transformed_credit)) {
|
||||
$entity[$keyval] = $transformed_credit[$key];
|
||||
} else {
|
||||
$entity[$keyval] = '';
|
||||
} elseif (array_key_exists($keyval, $transformed_credit)) {
|
||||
$entity[$keyval] = $transformed_credit[$keyval];
|
||||
}
|
||||
else {
|
||||
$entity[$keyval] = $this->resolveKey($keyval, $credit, $this->credit_transformer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,9 +147,9 @@ class CreditExport extends BaseExport
|
||||
if (in_array('country_id', $this->input['report_keys'])) {
|
||||
$entity['country'] = $credit->client->country ? ctrans("texts.country_{$credit->client->country->name}") : '';
|
||||
}
|
||||
|
||||
|
||||
if (in_array('currency_id', $this->input['report_keys'])) {
|
||||
$entity['currency_id'] = $credit->client->currency() ? $credit->client->currency()->code : $invoice->company->currency()->code;
|
||||
$entity['currency_id'] = $credit->client->currency() ? $credit->client->currency()->code : $credit->company->currency()->code;
|
||||
}
|
||||
|
||||
if (in_array('invoice_id', $this->input['report_keys'])) {
|
||||
@ -155,6 +164,10 @@ class CreditExport extends BaseExport
|
||||
$entity['status'] = $credit->stringStatus($credit->status_id);
|
||||
}
|
||||
|
||||
if(in_array('credit.status', $this->input['report_keys'])) {
|
||||
$entity['credit.status'] = $credit->stringStatus($credit->status_id);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ class InvoiceExport extends BaseExport
|
||||
'project',
|
||||
];
|
||||
|
||||
|
||||
public function __construct(Company $company, array $input)
|
||||
{
|
||||
$this->company = $company;
|
||||
@ -134,10 +135,21 @@ class InvoiceExport extends BaseExport
|
||||
foreach (array_values($this->input['report_keys']) as $key) {
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
|
||||
if(!$keyval) {
|
||||
$keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $key;
|
||||
}
|
||||
|
||||
if(!$keyval) {
|
||||
$keyval = $key;
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $transformed_invoice)) {
|
||||
$entity[$keyval] = $transformed_invoice[$key];
|
||||
} else {
|
||||
$entity[$keyval] = '';
|
||||
} elseif (array_key_exists($keyval, $transformed_invoice)) {
|
||||
$entity[$keyval] = $transformed_invoice[$keyval];
|
||||
}
|
||||
else {
|
||||
$entity[$keyval] = $this->resolveKey($keyval, $invoice, $this->invoice_transformer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,15 +174,15 @@ class InvoiceExport extends BaseExport
|
||||
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
||||
}
|
||||
|
||||
$payment_exists = $invoice->payments()->exists();
|
||||
// $payment_exists = $invoice->payments()->exists();
|
||||
|
||||
$entity['payment_number'] = $payment_exists ? $invoice->payments()->pluck('number')->implode(',') : '';
|
||||
// $entity['payment_number'] = $payment_exists ? $invoice->payments()->pluck('number')->implode(',') : '';
|
||||
|
||||
$entity['payment_date'] = $payment_exists ? $invoice->payments()->pluck('date')->implode(',') : '';
|
||||
// $entity['payment_date'] = $payment_exists ? $invoice->payments()->pluck('date')->implode(',') : '';
|
||||
|
||||
$entity['payment_amount'] = $payment_exists ? Number::formatMoney($invoice->payments()->sum('paymentables.amount'), $invoice->company) : ctrans('texts.unpaid');
|
||||
// $entity['payment_amount'] = $payment_exists ? Number::formatMoney($invoice->payments()->sum('paymentables.amount'), $invoice->company) : ctrans('texts.unpaid');
|
||||
|
||||
$entity['method'] = $payment_exists ? $invoice->payments()->first()->translatedType() : "";
|
||||
// $entity['method'] = $payment_exists ? $invoice->payments()->first()->translatedType() : "";
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ class InvoiceItemExport extends BaseExport
|
||||
|
||||
public Writer $csv;
|
||||
|
||||
private bool $force_keys = false;
|
||||
|
||||
public array $entity_keys = [
|
||||
'amount' => 'amount',
|
||||
'balance' => 'balance',
|
||||
@ -67,9 +69,9 @@ class InvoiceItemExport extends BaseExport
|
||||
'total_taxes' => 'total_taxes',
|
||||
'currency' => 'currency_id',
|
||||
'quantity' => 'item.quantity',
|
||||
'unit_cost' => 'item.cost',
|
||||
'cost' => 'item.cost',
|
||||
'product_key' => 'item.product_key',
|
||||
'cost' => 'item.product_cost',
|
||||
'buy_price' => 'item.product_cost',
|
||||
'notes' => 'item.notes',
|
||||
'discount' => 'item.discount',
|
||||
'is_amount_discount' => 'item.is_amount_discount',
|
||||
@ -85,6 +87,8 @@ class InvoiceItemExport extends BaseExport
|
||||
'invoice2' => 'item.custom_value2',
|
||||
'invoice3' => 'item.custom_value3',
|
||||
'invoice4' => 'item.custom_value4',
|
||||
'tax_category' => 'item.tax_id',
|
||||
'type' => 'item.type_id',
|
||||
];
|
||||
|
||||
private array $decorate_keys = [
|
||||
@ -112,6 +116,7 @@ class InvoiceItemExport extends BaseExport
|
||||
$this->csv = Writer::createFromString();
|
||||
|
||||
if (count($this->input['report_keys']) == 0) {
|
||||
$this->force_keys = true;
|
||||
$this->input['report_keys'] = array_values($this->entity_keys);
|
||||
}
|
||||
|
||||
@ -140,24 +145,36 @@ class InvoiceItemExport extends BaseExport
|
||||
$transformed_items = [];
|
||||
|
||||
foreach ($invoice->line_items as $item) {
|
||||
$item_array = [];
|
||||
$item_array = [];
|
||||
|
||||
foreach (array_values($this->input['report_keys']) as $key) {
|
||||
foreach (array_values($this->input['report_keys']) as $key) { //items iterator produces item array
|
||||
|
||||
if (str_contains($key, "item.")) {
|
||||
|
||||
$key = str_replace("item.", "", $key);
|
||||
|
||||
$keyval = $key;
|
||||
|
||||
$keyval = str_replace("custom_value", "invoice", $key);
|
||||
|
||||
if($key == 'type_id')
|
||||
$keyval = 'type';
|
||||
|
||||
if($key == 'tax_id')
|
||||
$keyval = 'tax_category';
|
||||
|
||||
if (property_exists($item, $key)) {
|
||||
$item_array[$key] = $item->{$key};
|
||||
$item_array[$keyval] = $item->{$key};
|
||||
} else {
|
||||
$item_array[$key] = '';
|
||||
$item_array[$keyval] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$entity = [];
|
||||
|
||||
foreach (array_values($this->input['report_keys']) as $key) {
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
foreach (array_values($this->input['report_keys']) as $key) { //create an array of report keys only
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
|
||||
if (array_key_exists($key, $transformed_items)) {
|
||||
$entity[$keyval] = $transformed_items[$key];
|
||||
@ -182,10 +199,21 @@ class InvoiceItemExport extends BaseExport
|
||||
foreach (array_values($this->input['report_keys']) as $key) {
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
|
||||
if(!$keyval) {
|
||||
$keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $key;
|
||||
}
|
||||
|
||||
if(!$keyval) {
|
||||
$keyval = $key;
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $transformed_invoice)) {
|
||||
$entity[$keyval] = $transformed_invoice[$key];
|
||||
} else {
|
||||
$entity[$keyval] = "";
|
||||
} elseif (array_key_exists($keyval, $transformed_invoice)) {
|
||||
$entity[$keyval] = $transformed_invoice[$keyval];
|
||||
}
|
||||
else {
|
||||
$entity[$keyval] = $this->resolveKey($keyval, $invoice, $this->invoice_transformer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,13 +226,20 @@ class InvoiceItemExport extends BaseExport
|
||||
$entity['currency'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
|
||||
}
|
||||
|
||||
// if(in_array('client_id', $this->input['report_keys']))
|
||||
$entity['client'] = $invoice->client->present()->name();
|
||||
$entity['client_id_number'] = $invoice->client->id_number;
|
||||
$entity['client_number'] = $invoice->client->number;
|
||||
if(array_key_exists('type', $entity)) {
|
||||
$entity['type'] = $invoice->typeIdString($entity['type']);
|
||||
}
|
||||
|
||||
// if(in_array('status_id', $this->input['report_keys']))
|
||||
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
||||
if(array_key_exists('tax_category', $entity)) {
|
||||
$entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']);
|
||||
}
|
||||
|
||||
if($this->force_keys) {
|
||||
$entity['client'] = $invoice->client->present()->name();
|
||||
$entity['client_id_number'] = $invoice->client->id_number;
|
||||
$entity['client_number'] = $invoice->client->number;
|
||||
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
@ -112,10 +112,21 @@ class PaymentExport extends BaseExport
|
||||
foreach (array_values($this->input['report_keys']) as $key) {
|
||||
$keyval = array_search($key, $this->entity_keys);
|
||||
|
||||
if(!$keyval) {
|
||||
$keyval = array_search(str_replace("payment.", "", $key), $this->entity_keys) ?? $key;
|
||||
}
|
||||
|
||||
if(!$keyval) {
|
||||
$keyval = $key;
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $transformed_entity)) {
|
||||
$entity[$keyval] = $transformed_entity[$key];
|
||||
} else {
|
||||
$entity[$keyval] = '';
|
||||
} elseif (array_key_exists($keyval, $transformed_entity)) {
|
||||
$entity[$keyval] = $transformed_entity[$keyval];
|
||||
}
|
||||
else {
|
||||
$entity[$keyval] = $this->resolveKey($keyval, $payment, $this->entity_transformer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,6 +151,10 @@ class PaymentExport extends BaseExport
|
||||
$entity['currency'] = $payment->currency()->exists() ? $payment->currency->code : '';
|
||||
}
|
||||
|
||||
if (in_array('payment.currency', $this->input['report_keys'])) {
|
||||
$entity['payment.currency'] = $payment->currency()->exists() ? $payment->currency->code : '';
|
||||
}
|
||||
|
||||
if (in_array('exchange_currency_id', $this->input['report_keys'])) {
|
||||
$entity['exchange_currency'] = $payment->exchange_currency()->exists() ? $payment->exchange_currency->code : '';
|
||||
}
|
||||
@ -152,11 +167,19 @@ class PaymentExport extends BaseExport
|
||||
$entity['type'] = $payment->translatedType();
|
||||
}
|
||||
|
||||
if (in_array('payment.method', $this->input['report_keys'])) {
|
||||
$entity['payment.method'] = $payment->translatedType();
|
||||
}
|
||||
|
||||
if (in_array('payment.status', $this->input['report_keys'])) {
|
||||
$entity['payment.status'] = $payment->stringStatus($payment->status_id);
|
||||
}
|
||||
|
||||
if (in_array('gateway_type_id', $this->input['report_keys'])) {
|
||||
$entity['gateway'] = $payment->gateway_type ? $payment->gateway_type->name : 'Unknown Type';
|
||||
}
|
||||
|
||||
$entity['invoices'] = $payment->invoices()->exists() ? $payment->invoices->pluck('number')->implode(',') : '';
|
||||
// $entity['invoices'] = $payment->invoices()->exists() ? $payment->invoices->pluck('number')->implode(',') : '';
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
@ -95,6 +95,11 @@ class RecurringExpenseToExpenseFactory
|
||||
|
||||
$replacements = [
|
||||
'literal' => [
|
||||
':MONTHYEAR' => \sprintf(
|
||||
'%s %s',
|
||||
Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F'),
|
||||
now()->year,
|
||||
),
|
||||
':MONTH' => Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F'),
|
||||
':YEAR' => now()->year,
|
||||
':QUARTER' => 'Q'.now()->quarter,
|
||||
@ -240,6 +245,17 @@ class RecurringExpenseToExpenseFactory
|
||||
$output = \Carbon\Carbon::create()->month($output)->translatedFormat('F');
|
||||
}
|
||||
|
||||
if ($matches->keys()->first() == ':MONTHYEAR') {
|
||||
|
||||
$final_date = now()->addMonths($output-now()->month);
|
||||
|
||||
$output = \sprintf(
|
||||
'%s %s',
|
||||
$final_date->translatedFormat('F'),
|
||||
$final_date->year,
|
||||
);
|
||||
}
|
||||
|
||||
$value = preg_replace(
|
||||
$target,
|
||||
$output,
|
||||
|
@ -115,6 +115,11 @@ class CreditFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
if ($sort_col[0] == 'client_id') {
|
||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||
->whereColumn('clients.id', 'credits.client_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,29 @@ class ExpenseFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter expenses that only have invoices
|
||||
*
|
||||
* @param string $value
|
||||
* @return Builder
|
||||
*/
|
||||
public function has_invoices(string $value = ''): Builder
|
||||
{
|
||||
$split = explode(",", $value);
|
||||
|
||||
if (is_array($split) && in_array($split[0], ['client', 'project'])) {
|
||||
|
||||
$search_key = $split[0] == 'client' ? 'client_id' : 'project_id';
|
||||
|
||||
return $this->builder->whereHas('invoice', function ($query) use ($search_key, $split){
|
||||
$query->where($search_key, $this->decodePrimaryKey($split[1]))
|
||||
->whereIn('status_id', [\App\Models\Invoice::STATUS_DRAFT, \App\Models\Invoice::STATUS_SENT, \App\Models\Invoice::STATUS_PARTIAL]);
|
||||
});
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of expenses that can be matched to bank transactions
|
||||
*/
|
||||
|
@ -11,12 +11,14 @@
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use RuntimeException;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Filters\QueryFilters;
|
||||
use InvalidArgumentException;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* InvoiceFilters.
|
||||
@ -205,10 +207,9 @@ class InvoiceFilters extends QueryFilters
|
||||
}
|
||||
|
||||
if ($sort_col[0] == 'client_id') {
|
||||
|
||||
$this->builder->with(['client' => function($q) use($sort_col){
|
||||
$q->orderBy('name', $sort_col[1]);
|
||||
}]);
|
||||
|
||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||
->whereColumn('clients.id', 'invoices.client_id'), $sort_col[1]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -158,6 +158,13 @@ class PaymentFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
|
||||
if ($sort_col[0] == 'client_id') {
|
||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||
->whereColumn('clients.id', 'payments.client_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,13 @@ class QuoteFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
if($sort_col[0] == 'client_id'){
|
||||
|
||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||
->whereColumn('clients.id', 'quotes.client_id'), $sort_col[1]);
|
||||
|
||||
}
|
||||
|
||||
if ($sort_col[0] == 'valid_until') {
|
||||
$sort_col[0] = 'due_date';
|
||||
}
|
||||
|
@ -115,6 +115,13 @@ class RecurringInvoiceFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
|
||||
if ($sort_col[0] == 'client_id') {
|
||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||
->whereColumn('clients.id', 'recurring_invoices.client_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
|
@ -110,9 +110,34 @@ class TaskFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
if ($sort_col[0] == 'client_id') {
|
||||
return $this->builder->orderBy(\App\Models\Client::select('name')
|
||||
->whereColumn('clients.id', 'tasks.client_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
if ($sort_col[0] == 'user_id') {
|
||||
return $this->builder->orderBy(\App\Models\User::select('first_name')
|
||||
->whereColumn('users.id', 'tasks.user_id'), $sort_col[1]);
|
||||
}
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
public function task_status(string $value = ''): Builder
|
||||
{
|
||||
if (strlen($value) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$status_parameters = explode(',', $value);
|
||||
|
||||
if(count($status_parameters) > 0)
|
||||
return $this->builder->whereIn('status_id', $this->transformKeys($status_parameters));
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters the query by the users company ID.
|
||||
*
|
||||
|
@ -36,6 +36,12 @@ class TokenFilters extends QueryFilters
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function is_system(bool $value = false): Builder
|
||||
{
|
||||
return $this->builder->where('is_system', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the list based on $sort.
|
||||
*
|
||||
|
@ -89,6 +89,15 @@ class UserFilters extends QueryFilters
|
||||
->where('account_id', auth()->user()->account_id);
|
||||
}
|
||||
|
||||
public function sending_users(string $value = ''): Builder
|
||||
{
|
||||
if (strlen($value) == 0 || $value != 'true') {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
return $this->builder->whereNotNull('oauth_user_refresh_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude a list of user_ids, can pass multiple
|
||||
* user IDs by separating them with a comma.
|
||||
|
@ -127,9 +127,12 @@ class IncomeTransformer implements BankRevenueInterface
|
||||
|
||||
foreach ($transaction->transaction as $transaction) {
|
||||
//do not store duplicate / pending transactions
|
||||
if (property_exists($transaction, 'status') && $transaction->status == 'PENDING') {
|
||||
if (property_exists($transaction, 'status') && $transaction->status == 'PENDING')
|
||||
continue;
|
||||
|
||||
//some object do no store amounts ignore these
|
||||
if(!property_exists($transaction, 'amount'))
|
||||
continue;
|
||||
}
|
||||
|
||||
$data[] = $this->transformTransaction($transaction);
|
||||
}
|
||||
@ -148,7 +151,7 @@ class IncomeTransformer implements BankRevenueInterface
|
||||
'category_type' => $transaction->categoryType,
|
||||
'date' => $transaction->date,
|
||||
'bank_account_id' => $transaction->accountId,
|
||||
'description' => $transaction->description->original,
|
||||
'description' => $transaction?->description?->original ?? '',
|
||||
'base_type' => property_exists($transaction, 'baseType') ? $transaction->baseType : $this->calculateBaseType($transaction),
|
||||
];
|
||||
}
|
||||
|
@ -85,42 +85,68 @@ class ActivityController extends BaseController
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$default_activities = $request->has('rows') ? $request->input('rows') : 50;
|
||||
$default_activities = $request->has('rows') ? $request->input('rows') : 75;
|
||||
|
||||
$activities = Activity::with('user')
|
||||
->orderBy('created_at', 'DESC')
|
||||
->company()
|
||||
->take($default_activities);
|
||||
|
||||
if ($request->has('react')) {
|
||||
if (!auth()->user()->isAdmin()) {
|
||||
// if ($request->has('react')) {
|
||||
|
||||
// /** @var \App\Models\User auth()->user() */
|
||||
// $user = auth()->user();
|
||||
|
||||
// if (!$user->isAdmin()) {
|
||||
// $activities->where('user_id', auth()->user()->id);
|
||||
// }
|
||||
|
||||
// $system = ctrans('texts.system');
|
||||
|
||||
// $data = $activities->cursor()->map(function ($activity) {
|
||||
|
||||
// $arr =
|
||||
// [
|
||||
// 'client' => $activity->client ? $activity->client : '',
|
||||
// 'contact' => $activity->client ? $activity->contact : '',
|
||||
// 'quote' => $activity->quote ? $activity->quote : '',
|
||||
// 'user' => $activity->user ? $activity->user : '',
|
||||
// 'expense' => $activity->expense ? $activity->expense : '',
|
||||
// 'invoice' => $activity->invoice ? $activity->invoice : '',
|
||||
// 'recurring_invoice' => $activity->recurring_invoice ? $activity->recurring_invoice : '',
|
||||
// 'payment' => $activity->payment ? $activity->payment : '',
|
||||
// 'credit' => $activity->credit ? $activity->credit : '',
|
||||
// 'task' => $activity->task ? $activity->task : '',
|
||||
// 'vendor' => $activity->vendor ? $activity->vendor : '',
|
||||
// 'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '',
|
||||
// 'subscription' => $activity->subscription ? $activity->subscription : '',
|
||||
// 'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
|
||||
// 'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense : '',
|
||||
// ];
|
||||
|
||||
// $activity_array = $activity->toArray();
|
||||
|
||||
// return array_merge($arr, $activity_array);
|
||||
// });
|
||||
|
||||
// return response()->json(['data' => $data->toArray()], 200);
|
||||
// }
|
||||
// else
|
||||
if($request->has('reactv2')) {
|
||||
|
||||
/** @var \App\Models\User auth()->user() */
|
||||
$user = auth()->user();
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
$activities->where('user_id', auth()->user()->id);
|
||||
}
|
||||
// return response()->json(['data' => []], 200);
|
||||
|
||||
$system = ctrans('texts.system');
|
||||
|
||||
$data = $activities->cursor()->map(function ($activity) use ($system) {
|
||||
$arr =
|
||||
[
|
||||
'client' => $activity->client ? $activity->client : '',
|
||||
'contact' => $activity->contact ? $activity->contact : '',
|
||||
'quote' => $activity->quote ? $activity->quote : '',
|
||||
'user' => $activity->user ? $activity->user : '',
|
||||
'expense' => $activity->expense ? $activity->expense : '',
|
||||
'invoice' => $activity->invoice ? $activity->invoice : '',
|
||||
'recurring_invoice' => $activity->recurring_invoice ? $activity->recurring_invoice : '',
|
||||
'payment' => $activity->payment ? $activity->payment : '',
|
||||
'credit' => $activity->credit ? $activity->credit : '',
|
||||
'task' => $activity->task ? $activity->task : '',
|
||||
'vendor' => $activity->vendor ? $activity->vendor : '',
|
||||
'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '',
|
||||
'subscription' => $activity->subscription ? $activity->subscription : '',
|
||||
'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
|
||||
'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense : '',
|
||||
];
|
||||
|
||||
return array_merge($arr, $activity->toArray());
|
||||
return $activity->activity_string();
|
||||
|
||||
});
|
||||
|
||||
return response()->json(['data' => $data->toArray()], 200);
|
||||
|
@ -682,8 +682,6 @@ class LoginController extends BaseController
|
||||
'email' => $socialite_user->getEmail(),
|
||||
'oauth_user_id' => $socialite_user->getId(),
|
||||
'oauth_provider_id' => $provider,
|
||||
// 'oauth_user_token' => $oauth_user_token,
|
||||
// 'oauth_user_refresh_token' => $socialite_user->refreshToken,
|
||||
];
|
||||
|
||||
$user->update($update_user);
|
||||
@ -699,7 +697,7 @@ class LoginController extends BaseController
|
||||
|
||||
$request_from_react = Cache::pull("react_redir:".auth()->user()?->account?->key);
|
||||
|
||||
if($request_from_react)
|
||||
// if($request_from_react)
|
||||
$redirect_url = config('ninja.react_url')."/#/settings/user_details/connect";
|
||||
|
||||
return redirect($redirect_url);
|
||||
@ -735,6 +733,10 @@ class LoginController extends BaseController
|
||||
nlog('user not found for oauth');
|
||||
}
|
||||
|
||||
return redirect('/#/');
|
||||
$redirect_url = config('ninja.react_url')."/#/settings/user_details/connect";
|
||||
|
||||
return redirect($redirect_url);
|
||||
|
||||
// return redirect('/#/');
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ class ChartController extends BaseController
|
||||
/** @var \App\Models\User auth()->user() */
|
||||
$user = auth()->user();
|
||||
$cs = new ChartService($user->company(), $user, $user->isAdmin());
|
||||
|
||||
|
||||
return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers;
|
||||
use App\Models\User;
|
||||
use App\Models\CompanyUser;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Transformers\UserTransformer;
|
||||
use App\Transformers\CompanyUserTransformer;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use App\Http\Requests\CompanyUser\UpdateCompanyUserRequest;
|
||||
@ -134,6 +135,7 @@ class CompanyUserController extends BaseController
|
||||
|
||||
public function updatePreferences(UpdateCompanyUserPreferencesRequest $request, User $user)
|
||||
{
|
||||
/** @var \App\Models\User $logged_in_user */
|
||||
$company = auth()->user()->company();
|
||||
|
||||
$company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first();
|
||||
@ -143,10 +145,14 @@ class CompanyUserController extends BaseController
|
||||
return;
|
||||
}
|
||||
|
||||
$this->entity_type = User::class;
|
||||
|
||||
$this->entity_transformer = UserTransformer::class;
|
||||
|
||||
$company_user->react_settings = $request->react_settings;
|
||||
$company_user->save();
|
||||
|
||||
return $this->itemResponse($company_user->fresh());
|
||||
return $this->itemResponse($user->fresh());
|
||||
}
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ class ConnectedAccountController extends BaseController
|
||||
* Connect an OAuth account to a regular email/password combination account
|
||||
*
|
||||
* @param Request $request
|
||||
* @return User Refresh Feed.
|
||||
* @return JsonResponse.
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
@ -90,14 +90,15 @@ class ConnectedAccountController extends BaseController
|
||||
|
||||
private function handleMicrosoftOauth($request)
|
||||
{
|
||||
nlog($request->all());
|
||||
$access_token = false;
|
||||
$access_token = $request->has('access_token') ? $request->input('access_token') : $request->input('accessToken');
|
||||
|
||||
if (!$request->has('access_token')) {
|
||||
if (!$access_token) {
|
||||
return response()->json(['message' => 'No access_token parameter found!'], 400);
|
||||
}
|
||||
|
||||
$graph = new \Microsoft\Graph\Graph();
|
||||
$graph->setAccessToken($request->input('access_token'));
|
||||
$graph->setAccessToken($access_token);
|
||||
|
||||
$user = $graph->createRequest("GET", "/me")
|
||||
->setReturnType(Model\User::class)
|
||||
|
@ -162,16 +162,14 @@ class ImportController extends Controller
|
||||
$delimiters = [',', '.', ';'];
|
||||
$bestDelimiter = ' ';
|
||||
$count = 0;
|
||||
|
||||
foreach ($delimiters as $delimiter) {
|
||||
// if (substr_count($csvfile, $delimiter) > $count) {
|
||||
// $count = substr_count($csvfile, $delimiter);
|
||||
// $bestDelimiter = $delimiter;
|
||||
// }
|
||||
|
||||
if (substr_count(strstr($csvfile, "\n", true), $delimiter) > $count) {
|
||||
if (substr_count(strstr($csvfile, "\n", true), $delimiter) >= $count) {
|
||||
$count = substr_count($csvfile, $delimiter);
|
||||
$bestDelimiter = $delimiter;
|
||||
}
|
||||
|
||||
}
|
||||
return $bestDelimiter;
|
||||
}
|
||||
|
@ -408,7 +408,7 @@ class InvoiceController extends BaseController
|
||||
|
||||
$invoice->service()
|
||||
->triggeredActions($request)
|
||||
->touchPdf()
|
||||
->deletePdf()
|
||||
->adjustInventory($old_invoice);
|
||||
|
||||
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
@ -740,7 +740,8 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
break;
|
||||
case 'cancel':
|
||||
$invoice = $invoice->service()->handleCancellation()->touchPdf()->save();
|
||||
$invoice = $invoice->service()->handleCancellation()->deletePdf()->save();
|
||||
// $invoice = $invoice->service()->handleCancellation()->touchPdf()->save();
|
||||
|
||||
if (! $bulk) {
|
||||
$this->itemResponse($invoice);
|
||||
@ -818,17 +819,11 @@ class InvoiceController extends BaseController
|
||||
return response()->json(['message' => 'no record found'], 400);
|
||||
}
|
||||
|
||||
$contact = $invitation->contact;
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
// $file = $invoice->service()->getInvoicePdf($contact);
|
||||
$file_name = $invoice->numberFormatter().'.pdf';
|
||||
|
||||
/************** */
|
||||
$file_name = $invoice->numberFormatter().'.pdf';
|
||||
|
||||
$file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle();
|
||||
|
||||
/************* */
|
||||
$file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle();
|
||||
|
||||
$headers = ['Content-Type' => 'application/pdf'];
|
||||
|
||||
|
@ -565,10 +565,11 @@ class RecurringInvoiceController extends BaseController
|
||||
return response()->json(['message' => 'no record found'], 400);
|
||||
}
|
||||
|
||||
$contact = $invitation->contact;
|
||||
$invoice = $invitation->recurring_invoice;
|
||||
|
||||
$file = $invoice->service()->getInvoicePdf($contact);
|
||||
$file_name = $invoice->numberFormatter().'.pdf';
|
||||
|
||||
$file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle();
|
||||
|
||||
$headers = ['Content-Type' => 'application/pdf'];
|
||||
|
||||
@ -577,8 +578,9 @@ class RecurringInvoiceController extends BaseController
|
||||
}
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), $headers);
|
||||
echo $file;
|
||||
}, $file_name, $headers);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -175,6 +175,7 @@ class UserController extends BaseController
|
||||
$user->oauth_user_refresh_token = null;
|
||||
$user->oauth_user_token = null;
|
||||
$user->save();
|
||||
|
||||
UserEmailChanged::dispatch($new_user, json_decode($old_user), $logged_in_user->company());
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,12 @@
|
||||
namespace App\Http\Requests\Chart;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
|
||||
class ShowChartRequest extends Request
|
||||
{
|
||||
use MakesDates;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@ -22,14 +25,18 @@ class ShowChartRequest extends Request
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
/**@var \App\Models\User auth()->user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'start_date' => 'date',
|
||||
'end_date' => 'date',
|
||||
'date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom',
|
||||
'start_date' => 'bail|sometimes|date',
|
||||
'end_date' => 'bail|sometimes|date',
|
||||
];
|
||||
}
|
||||
|
||||
@ -37,12 +44,18 @@ class ShowChartRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (! array_key_exists('start_date', $input)) {
|
||||
$input['start_date'] = now()->subDays(20);
|
||||
if(isset($input['date_range'])) {
|
||||
$dates = $this->calculateStartAndEndDates($input);
|
||||
$input['start_date'] = $dates[0];
|
||||
$input['end_date'] = $dates[1];
|
||||
}
|
||||
|
||||
if (! array_key_exists('end_date', $input)) {
|
||||
$input['end_date'] = now();
|
||||
if (! isset($input['start_date'])) {
|
||||
$input['start_date'] = now()->subDays(20)->format('Y-m-d');
|
||||
}
|
||||
|
||||
if (! isset($input['end_date'])) {
|
||||
$input['end_date'] = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
|
@ -48,7 +48,7 @@ class PreviewInvoiceRequest extends Request
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
$input['amount'] = 0;
|
||||
$input['balance'] = 0;
|
||||
$input['number'] = ctrans('texts.live_preview').' #'.rand(0, 1000);
|
||||
$input['number'] = isset($input['number']) ? $input['number'] : ctrans('texts.live_preview').' #'.rand(0, 1000);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class PreviewPurchaseOrderRequest extends Request
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
$input['amount'] = 0;
|
||||
$input['balance'] = 0;
|
||||
$input['number'] = ctrans('texts.live_preview') . " #". rand(0, 1000);
|
||||
$input['number'] = isset($input['number']) ? $input['number'] : ctrans('texts.live_preview').' #'.rand(0, 1000); //30-06-2023
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class GenericReportRequest extends Request
|
||||
'start_date' => 'bail|required_if:date_range,custom|nullable|date',
|
||||
'report_keys' => 'present|array',
|
||||
'send_email' => 'required|bool',
|
||||
'status' => 'sometimes|string|nullable|in:all,draft,sent,viewed,paid,unpaid,overdue',
|
||||
// 'status' => 'sometimes|string|nullable|in:all,draft,sent,viewed,paid,unpaid,overdue',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ class StoreSchedulerRequest extends Request
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('next_run', $input) && is_string($input['next_run'])) {
|
||||
$this->merge(['next_run_client' => $input['next_run']]);
|
||||
$input['next_run_client'] = $input['next_run'];
|
||||
}
|
||||
|
||||
if($input['template'] == 'email_record'){
|
||||
|
@ -54,7 +54,7 @@ class UpdateSchedulerRequest extends Request
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('next_run', $input) && is_string($input['next_run'])) {
|
||||
$this->merge(['next_run_client' => $input['next_run']]);
|
||||
$input['next_run_client'] = $input['next_run'];
|
||||
}
|
||||
|
||||
if($input['template'] == 'email_record') {
|
||||
|
@ -20,12 +20,14 @@ class BankTransactionMap
|
||||
1 => 'transaction.amount',
|
||||
2 => 'transaction.currency',
|
||||
3 => 'transaction.account_type',
|
||||
4 => 'transaction.category_id',
|
||||
4 => 'transaction.category',
|
||||
5 => 'transaction.category_type',
|
||||
6 => 'transaction.date',
|
||||
7 => 'transaction.bank_account_id',
|
||||
7 => 'transaction.bank_account',
|
||||
8 => 'transaction.description',
|
||||
9 => 'transaction.base_type',
|
||||
10 => 'transaction.payment_type_Credit',
|
||||
11 => 'transaction.payment_type_Debit',
|
||||
];
|
||||
}
|
||||
|
||||
@ -42,6 +44,8 @@ class BankTransactionMap
|
||||
7 => 'texts.bank_account_id',
|
||||
8 => 'texts.description',
|
||||
9 => 'texts.type',
|
||||
10 => 'transaction.credit',
|
||||
11 => 'transaction.debit',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,10 @@ class BaseImport
|
||||
|
||||
public function getCsvData($entity_type)
|
||||
{
|
||||
if (! ini_get('auto_detect_line_endings')) {
|
||||
ini_set('auto_detect_line_endings', '1');
|
||||
}
|
||||
|
||||
$base64_encoded_csv = Cache::pull($this->hash.'-'.$entity_type);
|
||||
|
||||
if (empty($base64_encoded_csv)) {
|
||||
@ -132,14 +136,12 @@ class BaseImport
|
||||
$bestDelimiter = ',';
|
||||
$count = 0;
|
||||
foreach ($delimiters as $delimiter) {
|
||||
// if (substr_count($csvfile, $delimiter) > $count) {
|
||||
// $count = substr_count($csvfile, $delimiter);
|
||||
// $bestDelimiter = $delimiter;
|
||||
// }
|
||||
if (substr_count(strstr($csvfile, "\n", true), $delimiter) > $count) {
|
||||
|
||||
if (substr_count(strstr($csvfile, "\n", true), $delimiter) >= $count) {
|
||||
$count = substr_count($csvfile, $delimiter);
|
||||
$bestDelimiter = $delimiter;
|
||||
}
|
||||
|
||||
}
|
||||
return $bestDelimiter;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class BankTransformer extends BaseTransformer
|
||||
$transformed = [
|
||||
'bank_integration_id' => $transaction['transaction.bank_integration_id'],
|
||||
'transaction_id' => $this->getNumber($transaction, 'transaction.transaction_id'),
|
||||
'amount' => abs($this->getFloat($transaction, 'transaction.amount')),
|
||||
'amount' => $this->calculateAmount($transaction),
|
||||
'currency_id' => $this->getCurrencyByCode($transaction, 'transaction.currency'),
|
||||
'account_type' => strlen($this->getString($transaction, 'transaction.account_type')) > 1 ? $this->getString($transaction, 'transaction.account_type') : 'bank',
|
||||
'category_id' => $this->getNumber($transaction, 'transaction.category_id') > 0 ? $this->getNumber($transaction, 'transaction.category_id') : null,
|
||||
@ -49,13 +49,35 @@ class BankTransformer extends BaseTransformer
|
||||
return $transformed;
|
||||
}
|
||||
|
||||
private function calculateAmount(array $transaction):float
|
||||
{
|
||||
|
||||
if (array_key_exists('transaction.amount', $transaction) && is_numeric($transaction['transaction.amount'])) {
|
||||
return abs($this->getFloat($transaction, 'transaction.amount'));
|
||||
}
|
||||
|
||||
if (array_key_exists('transaction.payment_type_Credit', $transaction) && is_numeric($transaction['transaction.payment_type_Credit'])) {
|
||||
return abs($this->getFloat($transaction, 'transaction.payment_type_Credit'));
|
||||
}
|
||||
|
||||
if (array_key_exists('transaction.payment_type_Debit', $transaction) && is_numeric($transaction['transaction.payment_type_Debit'])) {
|
||||
return abs($this->getFloat($transaction, 'transaction.payment_type_Debit'));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function calculateType($transaction)
|
||||
{
|
||||
if (array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'CREDIT') || strtolower($transaction['transaction.base_type']) == 'deposit')) {
|
||||
|
||||
if (array_key_exists('transaction.payment_type_Credit', $transaction) && is_numeric($transaction['transaction.payment_type_Credit'])) {
|
||||
return 'CREDIT';
|
||||
}
|
||||
|
||||
if (array_key_exists('transaction.transaction.payment_type_Debit', $transaction) && is_numeric($transaction['transaction.payment_type_Debit'])) {
|
||||
return 'DEBIT';
|
||||
}
|
||||
|
||||
if (array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'DEBIT') || strtolower($transaction['transaction.base_type']) == 'withdrawal')) {
|
||||
return 'DEBIT';
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ class MatchBankTransactions implements ShouldQueue
|
||||
$this->invoice
|
||||
->service()
|
||||
->applyNumber()
|
||||
->touchPdf()
|
||||
->deletePdf()
|
||||
->save();
|
||||
|
||||
$payment->ledger()
|
||||
|
@ -11,17 +11,19 @@
|
||||
|
||||
namespace App\Jobs\Bank;
|
||||
|
||||
use App\Helpers\Bank\Yodlee\Yodlee;
|
||||
use App\Models\Company;
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\Company;
|
||||
use App\Services\Bank\BankMatchingService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use App\Helpers\Bank\Yodlee\Yodlee;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use App\Services\Bank\BankMatchingService;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use App\Notifications\Ninja\GenericNinjaAdminNotification;
|
||||
|
||||
class ProcessBankTransactions implements ShouldQueue
|
||||
{
|
||||
@ -70,6 +72,14 @@ class ProcessBankTransactions implements ShouldQueue
|
||||
$this->processTransactions();
|
||||
} catch(\Exception $e) {
|
||||
nlog("{$this->bank_integration_account_id} - exited abnormally => ". $e->getMessage());
|
||||
|
||||
$content = [
|
||||
"Processing transactions for account: {$this->bank_integration->account->key} failed",
|
||||
"Exception Details => ",
|
||||
$e->getMessage(),
|
||||
];
|
||||
|
||||
$this->bank_integration->company->notification(new GenericNinjaAdminNotification($content))->ninja();
|
||||
return;
|
||||
}
|
||||
} while ($this->stop_loop);
|
||||
@ -152,4 +162,15 @@ class ProcessBankTransactions implements ShouldQueue
|
||||
$this->bank_integration->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [new WithoutOverlapping($this->bank_integration_account_id)];
|
||||
}
|
||||
|
||||
public function backoff()
|
||||
{
|
||||
return [rand(10, 15), rand(30, 40), rand(60, 79), rand(160, 200), rand(3000, 5000)];
|
||||
}
|
||||
}
|
||||
|
@ -73,12 +73,11 @@ class UpdateTaxData implements ShouldQueue
|
||||
nlog("problem getting tax data => ".$e->getMessage());
|
||||
}
|
||||
|
||||
/** Set static tax information */
|
||||
/*
|
||||
if(!$tax_provider->updatedTaxStatus() && $this->client->country_id == 840){
|
||||
|
||||
$calculated_state = false;
|
||||
|
||||
/** State must be calculated else default to the company state for taxes */
|
||||
if(array_key_exists($this->client->shipping_state, USStates::get())) {
|
||||
$calculated_state = $this->client->shipping_state;
|
||||
$calculated_postal_code = $this->client->shipping_postal_code;
|
||||
@ -136,7 +135,7 @@ class UpdateTaxData implements ShouldQueue
|
||||
$this->client->saveQuietly();
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
public function middleware()
|
||||
@ -147,6 +146,8 @@ class UpdateTaxData implements ShouldQueue
|
||||
public function failed($exception)
|
||||
{
|
||||
nlog("UpdateTaxData failed => ".$exception->getMessage());
|
||||
config(['queue.failed.driver' => null]);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -243,19 +243,20 @@ class NinjaMailerJob implements ShouldQueue
|
||||
case 'gmail':
|
||||
$this->mailer = 'gmail';
|
||||
$this->setGmailMailer();
|
||||
return;
|
||||
return $this;
|
||||
case 'office365':
|
||||
case 'microsoft':
|
||||
$this->mailer = 'office365';
|
||||
$this->setOfficeMailer();
|
||||
return;
|
||||
return $this;
|
||||
case 'client_postmark':
|
||||
$this->mailer = 'postmark';
|
||||
$this->setPostmarkMailer();
|
||||
return;
|
||||
return $this;
|
||||
case 'client_mailgun':
|
||||
$this->mailer = 'mailgun';
|
||||
$this->setMailgunMailer();
|
||||
return;
|
||||
return $this;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -264,6 +265,8 @@ class NinjaMailerJob implements ShouldQueue
|
||||
if (Ninja::isSelfHost()) {
|
||||
$this->setSelfHostMultiMailer();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,7 +65,7 @@ class EmailPayment implements ShouldQueue
|
||||
public function handle()
|
||||
{
|
||||
if ($this->company->is_disabled) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->contact->email) {
|
||||
@ -96,7 +96,7 @@ class EmailPayment implements ShouldQueue
|
||||
|
||||
(new NinjaMailerJob($nmo))->handle();
|
||||
|
||||
event(new PaymentWasEmailed($this->payment, $this->payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
event(new PaymentWasEmailed($this->payment, $this->payment->company, $this->contact, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class EmailRefundPayment implements ShouldQueue
|
||||
|
||||
(new NinjaMailerJob($nmo))->handle();
|
||||
|
||||
event(new PaymentWasEmailed($this->payment, $this->payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
event(new PaymentWasEmailed($this->payment, $this->payment->company, $this->contact, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,85 +11,89 @@
|
||||
|
||||
namespace App\Jobs\Util;
|
||||
|
||||
use App\DataMapper\Analytics\MigrationFailure;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Exceptions\ClientHostedMigrationException;
|
||||
use App\Exceptions\MigrationValidatorFailed;
|
||||
use App\Exceptions\ResourceDependencyMissing;
|
||||
use Exception;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\Company;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Document;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\PaymentTerm;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Factory\UserFactory;
|
||||
use App\Factory\QuoteFactory;
|
||||
use App\Models\ClientContact;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Factory\CompanyLedgerFactory;
|
||||
use App\Factory\CreditFactory;
|
||||
use App\Factory\VendorFactory;
|
||||
use App\Models\CompanyGateway;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Factory\ProductFactory;
|
||||
use App\Factory\QuoteFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Factory\TaxRateFactory;
|
||||
use App\Factory\UserFactory;
|
||||
use App\Factory\VendorFactory;
|
||||
use App\Http\Requests\Company\UpdateCompanyRequest;
|
||||
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
|
||||
use App\Http\ValidationRules\ValidUserForCompany;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Ninja\CheckCompanyData;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Migration\StripeConnectMigration;
|
||||
use App\Mail\MigrationCompleted;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Mail\MigrationCompleted;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Repositories\CompanyRepository;
|
||||
use App\Repositories\CreditRepository;
|
||||
use App\Repositories\Migration\InvoiceMigrationRepository;
|
||||
use App\Repositories\Migration\PaymentMigrationRepository;
|
||||
use App\Repositories\ProductRepository;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\Repositories\VendorContactRepository;
|
||||
use App\Repositories\VendorRepository;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Ninja\CheckCompanyData;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Factory\CompanyLedgerFactory;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Repositories\CreditRepository;
|
||||
use App\Repositories\VendorRepository;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
use App\Repositories\CompanyRepository;
|
||||
use App\Repositories\ProductRepository;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Modules\Admin\Jobs\Account\NinjaUser;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use App\DataMapper\ClientRegistrationFields;
|
||||
use App\Exceptions\MigrationValidatorFailed;
|
||||
use App\Exceptions\ResourceDependencyMissing;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\VendorContactRepository;
|
||||
use App\DataMapper\Analytics\MigrationFailure;
|
||||
use App\Mail\Migration\StripeConnectMigration;
|
||||
use App\Http\ValidationRules\ValidUserForCompany;
|
||||
use App\Exceptions\ClientHostedMigrationException;
|
||||
use App\Http\Requests\Company\UpdateCompanyRequest;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
|
||||
use App\Repositories\Migration\InvoiceMigrationRepository;
|
||||
use App\Repositories\Migration\PaymentMigrationRepository;
|
||||
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
|
||||
|
||||
class Import implements ShouldQueue
|
||||
{
|
||||
@ -135,6 +139,7 @@ class Import implements ShouldQueue
|
||||
'recurring_expenses',
|
||||
'tasks',
|
||||
'documents',
|
||||
'activities',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -181,7 +186,7 @@ class Import implements ShouldQueue
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [(new WithoutOverlapping($this->user->account_id))];
|
||||
return [(new WithoutOverlapping($this->company->company_key))];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,7 +308,7 @@ class Import implements ShouldQueue
|
||||
|
||||
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
|
||||
$client->paid_to_date = $total_invoice_payments;
|
||||
$client->save();
|
||||
$client->saveQuietly();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -319,12 +324,12 @@ class Import implements ShouldQueue
|
||||
$company_ledger->notes = 'Migrated Client Balance';
|
||||
$company_ledger->balance = $invoice_balances;
|
||||
$company_ledger->activity_id = Activity::CREATE_CLIENT;
|
||||
$company_ledger->save();
|
||||
$company_ledger->saveQuietly();
|
||||
|
||||
$client->company_ledger()->save($company_ledger);
|
||||
|
||||
$client->balance = $invoice_balances;
|
||||
$client->save();
|
||||
$client->saveQuietly();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1504,7 +1509,19 @@ class Import implements ShouldQueue
|
||||
false
|
||||
);
|
||||
|
||||
$this->saveDocument($uploaded_file, $entity, $is_public = true);
|
||||
// $this->saveDocument($uploaded_file, $entity, $is_public = true);
|
||||
|
||||
$document = (new \App\Jobs\Util\UploadFile(
|
||||
$uploaded_file,
|
||||
\App\Jobs\Util\UploadFile::DOCUMENT,
|
||||
$this->user,
|
||||
$this->company,
|
||||
$entity,
|
||||
null,
|
||||
true
|
||||
))->handle();
|
||||
|
||||
|
||||
} catch(\Exception $e) {
|
||||
//do nothing, gracefully :)
|
||||
}
|
||||
@ -1776,6 +1793,78 @@ class Import implements ShouldQueue
|
||||
$data = null;
|
||||
}
|
||||
|
||||
private function processActivities(array $data): void
|
||||
{
|
||||
Activity::where('company_id', $this->company->id)->cursor()->each(function ($a){
|
||||
$a->forceDelete();
|
||||
nlog("deleting {$a->id}");
|
||||
});
|
||||
|
||||
Activity::unguard();
|
||||
|
||||
foreach ($data as $resource) {
|
||||
$modified = $resource;
|
||||
|
||||
$modified['company_id'] = $this->company->id;
|
||||
$modified['user_id'] = $this->processUserId($resource);
|
||||
|
||||
try {
|
||||
if (isset($modified['client_id'])) {
|
||||
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['invoice_id'])) {
|
||||
$modified['invoice_id'] = $this->transformId('invoices', $resource['invoice_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['quote_id'])) {
|
||||
$modified['quote_id'] = $this->transformId('quotes', $resource['quote_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['recurring_invoice_id'])) {
|
||||
$modified['recurring_invoice_id'] = $this->transformId('recurring_invoices', $resource['recurring_invoice_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['payment_id'])) {
|
||||
$modified['payment_id'] = $this->transformId('payments', $resource['payment_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['credit_id'])) {
|
||||
$modified['credit_id'] = $this->transformId('credits', $resource['credit_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['expense_id'])) {
|
||||
$modified['expense_id'] = $this->transformId('expenses', $resource['expense_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['task_id'])) {
|
||||
$modified['task_id'] = $this->transformId('tasks', $resource['task_id']);
|
||||
}
|
||||
|
||||
if (isset($modified['client_contact_id'])) {
|
||||
$modified['client_contact_id'] = $this->transformId('client_contacts', $resource['client_contact_id']);
|
||||
}
|
||||
|
||||
$modified['updated_at'] = $modified['created_at'];
|
||||
|
||||
$act = Activity::make($modified);
|
||||
|
||||
$act->save(['timestamps' => false]);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
|
||||
nlog("could not import activity: {$e->getMessage()}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Activity::reguard();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function processExpenses(array $data) :void
|
||||
{
|
||||
Expense::unguard();
|
||||
|
@ -126,7 +126,7 @@ class ReminderJob implements ShouldQueue
|
||||
}
|
||||
|
||||
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||
nlog("reminder template = {$reminder_template}");
|
||||
// nlog("reminder template = {$reminder_template}");
|
||||
$invoice->service()->touchReminder($reminder_template)->save();
|
||||
$fees = $this->calcLateFee($invoice, $reminder_template);
|
||||
|
||||
@ -208,7 +208,8 @@ class ReminderJob implements ShouldQueue
|
||||
->markSent()
|
||||
->save();
|
||||
|
||||
$invoice->service()->touchPdf(true);
|
||||
//30-6-2023 - fix for duplicate touching
|
||||
// $invoice->service()->touchPdf(true);
|
||||
|
||||
$enabled_reminder = 'enable_'.$reminder_template;
|
||||
if ($reminder_template == 'endless_reminder') {
|
||||
@ -268,7 +269,6 @@ class ReminderJob implements ShouldQueue
|
||||
}
|
||||
|
||||
return [$late_fee_amount, $late_fee_percent];
|
||||
// return $this->setLateFee($invoice, $late_fee_amount, $late_fee_percent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,10 +60,6 @@ class StartMigration implements ShouldQueue
|
||||
|
||||
public $timeout = 0;
|
||||
|
||||
// public $maxExceptions = 2;
|
||||
|
||||
//public $backoff = 86430;
|
||||
|
||||
public function __construct($filepath, User $user, Company $company)
|
||||
{
|
||||
$this->filepath = $filepath;
|
||||
@ -159,8 +155,6 @@ class StartMigration implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
//always make sure we unset the migration as running
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,9 @@ class WebhookSingle implements ShouldQueue
|
||||
$client = new Client(['headers' => array_merge($base_headers, $headers)]);
|
||||
|
||||
try {
|
||||
$response = $client->post($subscription->target_url, [
|
||||
$verb = $subscription->rest_method ?? 'post';
|
||||
|
||||
$response = $client->{$verb}($subscription->target_url, [
|
||||
RequestOptions::JSON => $data, // or 'json' => [...]
|
||||
]);
|
||||
|
||||
|
@ -38,25 +38,28 @@ class PaymentCreatedActivity implements ShouldQueue
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
{
|
||||
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$payment = $event->payment;
|
||||
$invoice_id = null;
|
||||
|
||||
if($payment->invoices()->exists())
|
||||
$invoice_id = $payment->invoices()->first()->id;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->payment->user_id;
|
||||
|
||||
$invoices = $payment->invoices;
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$fields->payment_id = $payment->id;
|
||||
$fields->invoice_id = $invoice_id;
|
||||
$fields->client_id = $payment->client_id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $payment->company_id;
|
||||
$fields->activity_type_id = Activity::CREATE_PAYMENT;
|
||||
|
||||
if (count($invoices) == 0) {
|
||||
$this->activity_repo->save($fields, $payment, $event->event_vars);
|
||||
}
|
||||
$this->activity_repo->save($fields, $payment, $event->event_vars);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ class InvoiceEmailFailedActivity implements ShouldQueue
|
||||
$fields->client_contact_id = $event->invitation->client_contact_id;
|
||||
$fields->company_id = $event->invitation->invoice->company_id;
|
||||
$fields->activity_type_id = Activity::EMAIL_INVOICE_FAILED;
|
||||
$fields->notes = $event->message;
|
||||
|
||||
$this->activity_repo->save($fields, $event->invitation->invoice, $event->event_vars);
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ class PaymentEmailedActivity implements ShouldQueue
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->client_id = $event->payment->client_id;
|
||||
$fields->client_contact_id = $event->contact->id;
|
||||
$fields->company_id = $event->payment->company_id;
|
||||
$fields->activity_type_id = Activity::PAYMENT_EMAILED;
|
||||
$fields->payment_id = $event->payment->id;
|
||||
|
@ -46,7 +46,6 @@ class ArchivedUserActivity implements ShouldQueue
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->creating_user->id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->notes = $event->creating_user->present()->name.' Archived User '.$event->user->present()->name();
|
||||
|
||||
$fields->company_id = $event->company->id;
|
||||
$fields->activity_type_id = Activity::ARCHIVE_USER;
|
||||
|
@ -46,7 +46,6 @@ class CreatedUserActivity implements ShouldQueue
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->creating_user->id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->notes = $event->creating_user->present()->name().' Created the user '.$event->user->present()->name();
|
||||
$fields->company_id = $event->company->id;
|
||||
$fields->activity_type_id = Activity::CREATE_USER;
|
||||
|
||||
|
@ -51,8 +51,6 @@ class DeletedUserActivity implements ShouldQueue
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->creating_user->id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
|
||||
$fields->notes = $event->creating_user->present()->name().' Deleted the user '.$event->user->present()->name();
|
||||
$fields->company_id = $event->company->id;
|
||||
$fields->activity_type_id = Activity::DELETE_USER;
|
||||
|
||||
|
@ -45,7 +45,6 @@ class RestoredUserActivity implements ShouldQueue
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->creating_user->id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->notes = $event->creating_user->present()->name().' Restored user '.$event->user->present()->name();
|
||||
|
||||
$fields->company_id = $event->company->id;
|
||||
$fields->activity_type_id = Activity::RESTORE_USER;
|
||||
|
@ -45,7 +45,6 @@ class UpdatedUserActivity implements ShouldQueue
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->creating_user->id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->notes = $event->creating_user->present()->name().' Updated user '.$event->user->present()->name();
|
||||
|
||||
$fields->company_id = $event->company->id;
|
||||
$fields->activity_type_id = Activity::UPDATE_USER;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
/**
|
||||
@ -346,7 +347,7 @@ class Activity extends StaticModel
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(ClientContact::class)->withTrashed();
|
||||
return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -460,4 +461,90 @@ class Activity extends StaticModel
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function activity_string()
|
||||
{
|
||||
$intersect = [
|
||||
':invoice',
|
||||
':client',
|
||||
':contact',
|
||||
':user',
|
||||
':vendor',
|
||||
':quote',
|
||||
':credit',
|
||||
':payment',
|
||||
':task',
|
||||
':expense',
|
||||
':purchase_order',
|
||||
':subscription',
|
||||
':recurring_invoice',
|
||||
':recurring_expense',
|
||||
':amount',
|
||||
':balance',
|
||||
':number',
|
||||
':payment_amount',
|
||||
':gateway',
|
||||
':adjustment'
|
||||
];
|
||||
|
||||
$found_variables = array_intersect(explode(" ",trans("texts.activity_{$this->activity_type_id}")), $intersect);
|
||||
|
||||
$replacements = [];
|
||||
|
||||
foreach($found_variables as $var)
|
||||
$replacements = array_merge($replacements, $this->matchVar($var));
|
||||
|
||||
if($this->client)
|
||||
$replacements['client'] = ['label' => $this?->client?->present()->name() ?? '', 'hashed_id' => $this->client->hashed_id ?? ''];
|
||||
|
||||
if($this->vendor)
|
||||
$replacements['vendor'] = ['label' => $this?->vendor?->present()->name() ?? '', 'hashed_id' => $this->vendor->hashed_id ?? ''];
|
||||
|
||||
$replacements['activity_type_id'] = $this->activity_type_id;
|
||||
$replacements['id'] = $this->id;
|
||||
$replacements['hashed_id'] = $this->hashed_id;
|
||||
$replacements['notes'] = $this->notes ?? '';
|
||||
$replacements['created_at'] = $this->created_at ?? '';
|
||||
$replacements['ip'] = $this->ip ?? '';
|
||||
|
||||
return $replacements;
|
||||
|
||||
}
|
||||
|
||||
private function matchVar(string $variable)
|
||||
{
|
||||
$system = ctrans('texts.system');
|
||||
|
||||
match($variable) {
|
||||
':invoice' => $translation = [substr($variable, 1) => [ 'label' => $this?->invoice?->number ?? '', 'hashed_id' => $this->invoice?->hashed_id ?? '']],
|
||||
':user' => $translation = [substr($variable, 1) => [ 'label' => $this?->user?->present()->name() ?? $system, 'hashed_id' => $this->user->hashed_id ?? '']],
|
||||
':quote' => $translation = [substr($variable, 1) => [ 'label' => $this?->quote?->number ?? '', 'hashed_id' => $this->quote->hashed_id ?? '']],
|
||||
':credit' => $translation = [substr($variable, 1) => [ 'label' => $this?->credit?->number ?? '', 'hashed_id' => $this->credit->hashed_id ?? '']],
|
||||
':payment' => $translation = [substr($variable, 1) => [ 'label' => $this?->payment?->number ?? '', 'hashed_id' => $this->payment->hashed_id ?? '']],
|
||||
':task' => $translation = [substr($variable, 1) => [ 'label' => $this?->task?->number ?? '', 'hashed_id' => $this->task->hashed_id ?? '']],
|
||||
':expense' => $translation = [substr($variable, 1) => [ 'label' => $this?->expense?->number ?? '', 'hashed_id' => $this->expense->hashed_id ?? '']],
|
||||
':purchase_order' => $translation = [substr($variable, 1) => [ 'label' => $this?->purchase_order?->number ?? '', 'hashed_id' => $this->purchase_order->hashed_id ?? '']],
|
||||
':subscription' => $translation = [substr($variable, 1) => [ 'label' => $this?->subscription?->number ?? '', 'hashed_id' => $this->subscription->hashed_id ?? '' ]],
|
||||
':recurring_invoice' => $translation = [substr($variable, 1) =>[ 'label' => $this?->recurring_invoice?->number ??'', 'hashed_id' => $this->recurring_invoice->hashed_id ?? '']],
|
||||
':recurring_expense' => $translation = [substr($variable, 1) => [ 'label' => $this?->recurring_expense?->number ??'', 'hashed_id' => $this->recurring_expense->hashed_id ?? '']],
|
||||
':payment_amount' => $translation = [substr($variable, 1) =>[ 'label' => Number::formatMoney($this?->payment?->amount, $this?->payment?->client) ?? '', 'hashed_id' => '']],
|
||||
':adjustment' => $translation = [substr($variable, 1) =>[ 'label' => Number::formatMoney($this?->payment?->refunded, $this?->payment?->client) ?? '', 'hashed_id' => '']],
|
||||
':ip' => $translation = [ 'ip' => $this->ip ?? ''],
|
||||
':contact' => $translation = $this->resolveContact(),
|
||||
default => $translation = [],
|
||||
};
|
||||
|
||||
return $translation;
|
||||
}
|
||||
|
||||
private function resolveContact() : array
|
||||
{
|
||||
$contact = $this->contact ? $this->contact : $this->vendor_contact;
|
||||
|
||||
$entity = $this->contact ? $this->client : $this->vendor;
|
||||
|
||||
$contact_entity = $this->contact ? 'clients' : 'vendors';
|
||||
|
||||
return ['contact' => [ 'label' => $contact?->present()->name() ?? '', 'hashed_id' => $entity->hashed_id ?? '', 'contact_entity' => $contact_entity]];
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,17 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Jobs\Entity\CreateRawPdf;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
use App\Models\Traits\Excludable;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class BaseModel
|
||||
@ -45,8 +47,11 @@ use Illuminate\Support\Str;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel count()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel create()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel service()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereHas()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel withTrashed()
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\InvoiceInvitation | \App\Models\CreditInvitation | \App\Models\QuoteInvitation | \App\Models\RecurringInvoiceInvitation> $invitations
|
||||
* @property-read int|null $invitations_count
|
||||
*
|
||||
* @method \App\Models\Company company()
|
||||
* @method int companyId()
|
||||
@ -255,4 +260,30 @@ class BaseModel extends Model
|
||||
WebhookHandler::dispatch($event_id, $this, $this->company, $additional_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base64 encoded PDF string of the entity
|
||||
*/
|
||||
public function fullscreenPdfViewer($invitation = null): string
|
||||
{
|
||||
|
||||
if (! $invitation) {
|
||||
if ($this->invitations()->exists()) {
|
||||
$invitation = $this->invitations()->first();
|
||||
} else {
|
||||
$this->service()->createInvitations();
|
||||
$invitation = $this->invitations()->first();
|
||||
}
|
||||
}
|
||||
|
||||
if (! $invitation) {
|
||||
throw new \Exception('Hard fail, could not create an invitation.');
|
||||
}
|
||||
|
||||
if($this instanceof \App\Models\PurchaseOrder)
|
||||
return "data:application/pdf;base64,".base64_encode((new CreatePurchaseOrderPdf($invitation, $invitation->company->db))->rawPdf());
|
||||
|
||||
return "data:application/pdf;base64,".base64_encode((new CreateRawPdf($invitation, $invitation->company->db))->handle());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ use Laracasts\Presenter\PresentableTrait;
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientGatewayToken> $gateway_tokens
|
||||
* @property-read int|null $gateway_tokens_count
|
||||
* @property-read mixed $hashed_id
|
||||
* @property-read \App\Models\GroupSetting|null $group_settings
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Invoice> $invoices
|
||||
* @property-read int|null $invoices_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $ledger
|
||||
@ -112,6 +111,7 @@ use Laracasts\Presenter\PresentableTrait;
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\SystemLog> $system_logs
|
||||
* @property-read int|null $system_logs_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Task> $tasks
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\RecurringInvoice> $recurring_invoices
|
||||
* @property-read int|null $tasks_count
|
||||
* @property-read \App\Models\User $user
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
|
||||
@ -175,26 +175,7 @@ use Laracasts\Presenter\PresentableTrait;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Client whereTaxData($value)
|
||||
* @property int $is_tax_exempt
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Client whereIsTaxExempt($value)
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientContact> $contacts
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Credit> $credits
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Expense> $expenses
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\GroupSetting> $group_settings
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientGatewayToken> $gateway_tokens
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Invoice> $invoices
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $ledger
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Payment> $payments
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientContact> $primary_contact
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Project> $projects
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Quote> $quotes
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\RecurringExpense> $recurring_expenses
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\RecurringInvoice> $recurring_invoices
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\SystemLog> $system_logs
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Task> $tasks
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientContact> $contacts
|
||||
|
||||
* @property int $has_valid_vat_number
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
@ -464,6 +445,11 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
return $this->belongsTo(Industry::class);
|
||||
}
|
||||
|
||||
public function size()
|
||||
{
|
||||
return $this->belongsTo(Size::class);
|
||||
}
|
||||
|
||||
public function locale()
|
||||
{
|
||||
if (! $this->language()) {
|
||||
@ -544,9 +530,9 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
return $this->settings->{$setting};
|
||||
} elseif (is_bool($this->settings->{$setting})) {
|
||||
return $this->settings->{$setting};
|
||||
} elseif (is_int($this->settings->{$setting})) { //10-08-2022 integer client values are not being passed back! This resolves it.
|
||||
} elseif (is_int($this->settings->{$setting})) {
|
||||
return $this->settings->{$setting};
|
||||
} elseif(is_float($this->settings->{$setting})) { //10-08-2022 integer client values are not being passed back! This resolves it.
|
||||
} elseif(is_float($this->settings->{$setting})) {
|
||||
return $this->settings->{$setting};
|
||||
}
|
||||
}
|
||||
|
@ -212,29 +212,29 @@ class Company extends BaseModel
|
||||
use AppSetup;
|
||||
use \Awobaz\Compoships\Compoships;
|
||||
|
||||
const ENTITY_RECURRING_INVOICE = 'recurring_invoice';
|
||||
// const ENTITY_RECURRING_INVOICE = 'recurring_invoice';
|
||||
|
||||
const ENTITY_CREDIT = 'credit';
|
||||
// const ENTITY_CREDIT = 'credit';
|
||||
|
||||
const ENTITY_QUOTE = 'quote';
|
||||
// const ENTITY_QUOTE = 'quote';
|
||||
|
||||
const ENTITY_TASK = 'task';
|
||||
// const ENTITY_TASK = 'task';
|
||||
|
||||
const ENTITY_EXPENSE = 'expense';
|
||||
// const ENTITY_EXPENSE = 'expense';
|
||||
|
||||
const ENTITY_PROJECT = 'project';
|
||||
// const ENTITY_PROJECT = 'project';
|
||||
|
||||
const ENTITY_VENDOR = 'vendor';
|
||||
// const ENTITY_VENDOR = 'vendor';
|
||||
|
||||
const ENTITY_TICKET = 'ticket';
|
||||
// const ENTITY_TICKET = 'ticket';
|
||||
|
||||
const ENTITY_PROPOSAL = 'proposal';
|
||||
// const ENTITY_PROPOSAL = 'proposal';
|
||||
|
||||
const ENTITY_RECURRING_EXPENSE = 'recurring_expense';
|
||||
// const ENTITY_RECURRING_EXPENSE = 'recurring_expense';
|
||||
|
||||
const ENTITY_RECURRING_TASK = 'task';
|
||||
// const ENTITY_RECURRING_TASK = 'task';
|
||||
|
||||
const ENTITY_RECURRING_QUOTE = 'recurring_quote';
|
||||
// const ENTITY_RECURRING_QUOTE = 'recurring_quote';
|
||||
|
||||
protected $presenter = CompanyPresenter::class;
|
||||
|
||||
@ -309,7 +309,6 @@ class Company extends BaseModel
|
||||
'google_analytics_key',
|
||||
'matomo_url',
|
||||
'matomo_id',
|
||||
'enable_e_invoice',
|
||||
'client_can_register',
|
||||
'enable_shop_api',
|
||||
'invoice_task_timelog',
|
||||
@ -368,31 +367,26 @@ class Company extends BaseModel
|
||||
|
||||
protected $with = [];
|
||||
|
||||
public static $modules = [
|
||||
self::ENTITY_RECURRING_INVOICE => 1,
|
||||
self::ENTITY_CREDIT => 2,
|
||||
self::ENTITY_QUOTE => 4,
|
||||
self::ENTITY_TASK => 8,
|
||||
self::ENTITY_EXPENSE => 16,
|
||||
self::ENTITY_PROJECT => 32,
|
||||
self::ENTITY_VENDOR => 64,
|
||||
self::ENTITY_TICKET => 128,
|
||||
self::ENTITY_PROPOSAL => 256,
|
||||
self::ENTITY_RECURRING_EXPENSE => 512,
|
||||
self::ENTITY_RECURRING_TASK => 1024,
|
||||
self::ENTITY_RECURRING_QUOTE => 2048,
|
||||
];
|
||||
// public static $modules = [
|
||||
// self::ENTITY_RECURRING_INVOICE => 1,
|
||||
// self::ENTITY_CREDIT => 2,
|
||||
// self::ENTITY_QUOTE => 4,
|
||||
// self::ENTITY_TASK => 8,
|
||||
// self::ENTITY_EXPENSE => 16,
|
||||
// self::ENTITY_PROJECT => 32,
|
||||
// self::ENTITY_VENDOR => 64,
|
||||
// self::ENTITY_TICKET => 128,
|
||||
// self::ENTITY_PROPOSAL => 256,
|
||||
// self::ENTITY_RECURRING_EXPENSE => 512,
|
||||
// self::ENTITY_RECURRING_TASK => 1024,
|
||||
// self::ENTITY_RECURRING_QUOTE => 2048,
|
||||
// ];
|
||||
|
||||
public function shouldCalculateTax()
|
||||
{
|
||||
return $this->calculate_taxes && in_array($this->getSetting('country_id'), $this->tax_coverage_countries);
|
||||
}
|
||||
|
||||
public function refreshTaxData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function documents()
|
||||
{
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
|
@ -225,6 +225,11 @@ class Expense extends BaseModel
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class);
|
||||
}
|
||||
|
||||
public function vendor()
|
||||
{
|
||||
return $this->belongsTo(Vendor::class);
|
||||
|
@ -14,6 +14,7 @@ namespace App\Models;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Jobs\Entity\CreateRawPdf;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
@ -683,6 +684,7 @@ class Invoice extends BaseModel
|
||||
|
||||
public function pdf_file_path($invitation = null, string $type = 'path', bool $portal = false)
|
||||
{
|
||||
|
||||
if (! $invitation) {
|
||||
if ($this->invitations()->exists()) {
|
||||
$invitation = $this->invitations()->first();
|
||||
@ -725,7 +727,6 @@ class Invoice extends BaseModel
|
||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$file_exists = Storage::disk('public')->exists($file_path);
|
||||
} catch (\Exception $e) {
|
||||
@ -888,4 +889,38 @@ class Invoice extends BaseModel
|
||||
{
|
||||
return ctrans('texts.invoice');
|
||||
}
|
||||
|
||||
public function taxTypeString($id)
|
||||
{
|
||||
match(intval($id)){
|
||||
Product::PRODUCT_TYPE_PHYSICAL => $tax_type = ctrans('texts.physical_goods'),
|
||||
Product::PRODUCT_TYPE_SERVICE => $tax_type = ctrans('texts.services'),
|
||||
Product::PRODUCT_TYPE_DIGITAL => $tax_type = ctrans('texts.digital_products'),
|
||||
Product::PRODUCT_TYPE_SHIPPING => $tax_type = ctrans('texts.shipping'),
|
||||
Product::PRODUCT_TYPE_EXEMPT => $tax_type = ctrans('texts.tax_exempt'),
|
||||
Product::PRODUCT_TYPE_REDUCED_TAX => $tax_type = ctrans('texts.reduced_tax'),
|
||||
Product::PRODUCT_TYPE_OVERRIDE_TAX => $tax_type = ctrans('texts.override_tax'),
|
||||
Product::PRODUCT_TYPE_ZERO_RATED => $tax_type = ctrans('texts.zero_rated'),
|
||||
Product::PRODUCT_TYPE_REVERSE_TAX => $tax_type = ctrans('texts.reverse_tax'),
|
||||
default => $tax_type = ctrans('texts.physical_goods'),
|
||||
};
|
||||
|
||||
return $tax_type;
|
||||
}
|
||||
|
||||
public function typeIdString($id)
|
||||
{
|
||||
match($id) {
|
||||
'1' => $type = ctrans('texts.product'),
|
||||
'2' => $type = ctrans('texts.service'),
|
||||
'3' => $type = ctrans('texts.gateway_fees'),
|
||||
'4' => $type = ctrans('texts.gateway_fees'),
|
||||
'5' => $type = ctrans('texts.late_fees'),
|
||||
'6' => $type = ctrans('texts.expense'),
|
||||
default => $type = ctrans('texts.product'),
|
||||
};
|
||||
|
||||
return $type;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -522,17 +522,18 @@ class Payment extends BaseModel
|
||||
event(new PaymentWasVoided($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
|
||||
// public function getLink()
|
||||
// {
|
||||
// return route('client.payments.show', $this->hashed_id);
|
||||
// }
|
||||
|
||||
public function getLink() :string
|
||||
{
|
||||
// if (Ninja::isHosted()) {
|
||||
// $domain = isset($this->company->portal_domain) ? $this->company->portal_domain : $this->company->domain();
|
||||
// } else {
|
||||
// $domain = config('ninja.app_url');
|
||||
// }
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
$domain = isset($this->company->portal_domain) ? $this->company->portal_domain : $this->company->domain();
|
||||
$domain = $this->company->domain();
|
||||
} else {
|
||||
$domain = config('ninja.app_url');
|
||||
$domain = strlen($this->company->portal_domain) > 5 ? $this->company->portal_domain : config('ninja.app_url');
|
||||
}
|
||||
|
||||
return $domain.'/client/payment/'.$this->client->contacts()->first()->contact_key.'/'.$this->hashed_id.'?next=/client/payments/'.$this->hashed_id;
|
||||
|
@ -34,7 +34,8 @@ class PaymentType extends StaticModel
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
const CREDIT = 32;
|
||||
const BANK_TRANSFER = 1;
|
||||
const CASH = 2;
|
||||
const ACH = 4;
|
||||
const VISA = 5;
|
||||
const MASTERCARD = 6;
|
||||
@ -53,11 +54,14 @@ class PaymentType extends StaticModel
|
||||
const MAESTRO = 20;
|
||||
const SOLO = 21;
|
||||
const SWITCH = 22;
|
||||
const VENMO = 24;
|
||||
const ALIPAY = 27;
|
||||
const SOFORT = 28;
|
||||
const SEPA = 29;
|
||||
const GOCARDLESS = 30;
|
||||
const CRYPTO = 31;
|
||||
const CREDIT = 32;
|
||||
const ZELLE = 33;
|
||||
const MOLLIE_BANK_TRANSFER = 34;
|
||||
const KBC = 35;
|
||||
const BANCONTACT = 36;
|
||||
@ -76,10 +80,12 @@ class PaymentType extends StaticModel
|
||||
const BACS = 49;
|
||||
const STRIPE_BANK_TRANSFER = 50;
|
||||
const CASH_APP = 51;
|
||||
const VENMO = 24;
|
||||
|
||||
public array $type_names = [
|
||||
self::BANK_TRANSFER => 'payment_type_Bank Transfer',
|
||||
self::CASH => 'payment_type_Cash',
|
||||
self::CREDIT => 'payment_type_Credit',
|
||||
self::ZELLE => 'payment_type_Zelle',
|
||||
self::ACH => 'payment_type_ACH',
|
||||
self::VISA => 'payment_type_Visa Card',
|
||||
self::MASTERCARD => 'payment_type_MasterCard',
|
||||
|
@ -202,5 +202,16 @@ class Scheduler extends BaseModel
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function adjustOffset(): void
|
||||
{
|
||||
if (! $this->next_run) {
|
||||
return;
|
||||
}
|
||||
|
||||
$offset = $this->company->timezone_offset();
|
||||
|
||||
$this->next_run = $this->next_run->copy()->addSeconds($offset);
|
||||
$this->save();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundExceptio
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel find()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel with()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel findOrFail()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
|
71
app/Notifications/Ninja/GenericNinjaAdminNotification.php
Normal file
71
app/Notifications/Ninja/GenericNinjaAdminNotification.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Notifications\Ninja;
|
||||
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class GenericNinjaAdminNotification extends Notification
|
||||
{
|
||||
|
||||
public function __construct(protected array $message_array)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['slack'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack($notifiable)
|
||||
{
|
||||
$content = '';
|
||||
|
||||
foreach($this->message_array as $message) {
|
||||
$content .= $message . "\n";
|
||||
}
|
||||
|
||||
return (new SlackMessage)
|
||||
->success()
|
||||
->from(ctrans('texts.notification_bot'))
|
||||
->image('https://app.invoiceninja.com/favicon.png')
|
||||
->content($content);
|
||||
}
|
||||
}
|
@ -525,7 +525,7 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get();
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
$invoice->service()->touchPdf();
|
||||
$invoice->service()->deletePdf();
|
||||
});
|
||||
|
||||
$invoices->first()->invitations->each(function ($invitation) use ($nmo) {
|
||||
@ -570,7 +570,7 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get();
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
$invoice->service()->touchPdf();
|
||||
$invoice->service()->deletePdf();
|
||||
});
|
||||
|
||||
$invoices->first()->invitations->each(function ($invitation) use ($nmo) {
|
||||
@ -732,6 +732,9 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
|
||||
$invoices_string = \implode(', ', collect($this->payment_hash->invoices())->pluck('invoice_number')->toArray()) ?: null;
|
||||
|
||||
if (!$invoices_string)
|
||||
return str_replace(["*","<",">","'",'"'], "", $this->client->company->present()->name());
|
||||
|
||||
$invoices_string = str_replace(["*","<",">","'",'"'], "-", $invoices_string);
|
||||
|
||||
$invoices_string = "I-".$invoices_string;
|
||||
|
@ -70,12 +70,15 @@ class CreditCard
|
||||
|
||||
$response = $this->eway_driver->init()->eway->createCustomer(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction);
|
||||
|
||||
$response_status = ErrorCode::getStatus($response->ResponseMessage);
|
||||
if($response->getErrors()) {
|
||||
|
||||
$response_status['message'] = \Eway\Rapid::getMessage($response->getErrors()[0]);
|
||||
|
||||
if (! $response_status['success']) {
|
||||
$this->eway_driver->sendFailureMail($response_status['message']);
|
||||
|
||||
throw new PaymentFailed($response_status['message'], 400);
|
||||
$this->logResponse($response);
|
||||
|
||||
throw new PaymentFailed($response_status['message'] ?? 'Unknown response from gateway, please contact you merchant.', 400);
|
||||
}
|
||||
|
||||
//success
|
||||
@ -94,6 +97,8 @@ class CreditCard
|
||||
|
||||
$token = $this->eway_driver->storeGatewayToken($cgt, []);
|
||||
|
||||
$this->logResponse($response);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
@ -135,7 +140,7 @@ class CreditCard
|
||||
|
||||
$amount = array_sum(array_column($this->eway_driver->payment_hash->invoices(), 'amount')) + $this->eway_driver->payment_hash->fee_total;
|
||||
|
||||
$description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->eway_driver->client->present()->name()}";
|
||||
// $description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->eway_driver->client->present()->name()}";
|
||||
|
||||
$transaction = [
|
||||
'Payment' => [
|
||||
@ -152,29 +157,6 @@ class CreditCard
|
||||
|
||||
$this->logResponse($response);
|
||||
|
||||
// if(!$response || !property_exists($response, 'ResponseMessage'))
|
||||
// throw new PaymentFailed('The gateway did not return a valid response. Please check your gateway credentials.', 400);
|
||||
|
||||
// $response_status = ErrorCode::getStatus($response->ResponseMessage);
|
||||
|
||||
// if(!$response_status['success']){
|
||||
|
||||
// if($response->getErrors())
|
||||
// {
|
||||
// $message = false;
|
||||
|
||||
// foreach ($response->getErrors() as $error) {
|
||||
// $message = \Eway\Rapid::getMessage($error);
|
||||
// }
|
||||
|
||||
// $return_message = $message ?: $response_status['message'];
|
||||
// }
|
||||
|
||||
// $this->eway_driver->sendFailureMail($response_status['message']);
|
||||
|
||||
// throw new PaymentFailed($response_status['message'], 400);
|
||||
// }
|
||||
|
||||
if ($response->TransactionStatus) {
|
||||
$payment = $this->storePayment($response);
|
||||
} else {
|
||||
|
@ -123,6 +123,7 @@ class CreditCard
|
||||
'city' => $this->paytrace->client->city,
|
||||
'state' => $this->paytrace->client->state,
|
||||
'zip' => $this->paytrace->client->postal_code,
|
||||
'country' => $this->paytrace->client->country->iso_3166_2
|
||||
];
|
||||
|
||||
return $data;
|
||||
@ -177,6 +178,7 @@ class CreditCard
|
||||
'customer_id' => $token,
|
||||
'integrator_id' => $this->paytrace->company_gateway->getConfigField('integratorId'),
|
||||
'amount' => $request->input('amount_with_fee'),
|
||||
'invoice_id' => $this->harvestInvoiceId(),
|
||||
];
|
||||
|
||||
$response = $this->paytrace->gatewayRequest('/v1/transactions/sale/by_customer', $data);
|
||||
|
@ -106,7 +106,7 @@ class iDeal
|
||||
'gateway_type_id' => GatewayType::IDEAL,
|
||||
];
|
||||
|
||||
$this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||
$this->stripe->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $this->stripe->payment_hash->data, 'data' => $data],
|
||||
|
@ -122,7 +122,7 @@ class PaymentMigrationRepository extends BaseRepository
|
||||
|
||||
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->withTrashed()->get();
|
||||
|
||||
$payment->invoices()->saveMany($invoices);
|
||||
$payment->invoices()->saveMany($invoices); // 1:1 relationship so this is ok
|
||||
|
||||
$payment->invoices->each(function ($inv) use ($invoice_totals, $refund_totals, $payment) {
|
||||
if ($payment->status_id != Payment::STATUS_CANCELLED || ! $payment->is_deleted) {
|
||||
|
@ -11,19 +11,20 @@
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Events\Payment\PaymentWasDeleted;
|
||||
use App\Jobs\Credit\ApplyCreditPayment;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use App\Models\Paymentable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use App\Jobs\Credit\ApplyCreditPayment;
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Events\Payment\PaymentWasDeleted;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
|
||||
/**
|
||||
* PaymentRepository.
|
||||
@ -138,7 +139,7 @@ class PaymentRepository extends BaseRepository
|
||||
|
||||
$invoices = Invoice::withTrashed()->whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
||||
|
||||
$payment->invoices()->saveMany($invoices);
|
||||
// $payment->invoices()->saveMany($invoices); //25-06-2023
|
||||
|
||||
//todo optimize this into a single query
|
||||
foreach ($data['invoices'] as $paid_invoice) {
|
||||
@ -146,6 +147,16 @@ class PaymentRepository extends BaseRepository
|
||||
$invoice = $invoices->firstWhere('id', $paid_invoice['invoice_id']);
|
||||
|
||||
if ($invoice) {
|
||||
|
||||
//25-06-2023
|
||||
|
||||
$paymentable = new Paymentable();
|
||||
$paymentable->payment_id = $payment->id;
|
||||
$paymentable->paymentable_id = $invoice->id;
|
||||
$paymentable->paymentable_type = 'invoices';
|
||||
$paymentable->amount = $paid_invoice['amount'];
|
||||
$paymentable->save();
|
||||
|
||||
$invoice = $invoice->service()
|
||||
->markSent()
|
||||
->applyPayment($payment, $paid_invoice['amount'])
|
||||
@ -153,26 +164,30 @@ class PaymentRepository extends BaseRepository
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//payment is made, but not to any invoice, therefore we are applying the payment to the clients paid_to_date only
|
||||
//01-07-2020 i think we were duplicating the paid to date here.
|
||||
//$payment->client->service()->updatePaidToDate($payment->amount)->save();
|
||||
|
||||
}
|
||||
|
||||
if (array_key_exists('credits', $data) && is_array($data['credits'])) {
|
||||
$credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
|
||||
// $credits = Credit::whereIn('id', $this->transformKeys(array_column($data['credits'], 'credit_id')))->get();
|
||||
|
||||
$credits = Credit::whereIn('id', array_column($data['credits'], 'credit_id'))->get();
|
||||
|
||||
$payment->credits()->saveMany($credits);
|
||||
// $payment->credits()->saveMany($credits);
|
||||
|
||||
//todo optimize into a single query
|
||||
foreach ($data['credits'] as $paid_credit) {
|
||||
// $credit = Credit::withTrashed()->find($paid_credit['credit_id']);
|
||||
|
||||
$credit = $credits->firstWhere('id', $paid_credit['credit_id']);
|
||||
|
||||
if ($credit) {
|
||||
|
||||
$paymentable = new Paymentable();
|
||||
$paymentable->payment_id = $payment->id;
|
||||
$paymentable->paymentable_id = $credit->id;
|
||||
$paymentable->paymentable_type = Credit::class;
|
||||
$paymentable->amount = $paid_credit['amount'];
|
||||
$paymentable->save();
|
||||
|
||||
$credit = $credit->service()->markSent()->save();
|
||||
(new ApplyCreditPayment($credit, $payment, $paid_credit['amount'], $credit->company))->handle();
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ class SchedulerRepository extends BaseRepository
|
||||
|
||||
$scheduler->save();
|
||||
|
||||
/** 18-5-2023 set client specific send times. */
|
||||
$scheduler->calculateNextRun();
|
||||
$scheduler->adjustOffset();
|
||||
|
||||
return $scheduler->fresh();
|
||||
}
|
||||
|
@ -38,13 +38,15 @@ class TaskStatusRepository extends BaseRepository
|
||||
|
||||
public function archive($task_status)
|
||||
{
|
||||
$task_status = TaskStatus::where('id', $task_status->id)
|
||||
$task_status = TaskStatus::withTrashed()
|
||||
->where('id', $task_status->id)
|
||||
->where('company_id', $task_status->company_id)
|
||||
->first();
|
||||
|
||||
$new_status = $task_status ? $task_status->id : null;
|
||||
|
||||
Task::where('status_id', $task_status->id)
|
||||
Task::withTrashed()
|
||||
->where('status_id', $task_status->id)
|
||||
->where('company_id', $task_status->company_id)
|
||||
->update(['status_id' => $new_status]);
|
||||
|
||||
|
@ -76,6 +76,8 @@ class ChartService
|
||||
$currencies = $this->getCurrencyCodes();
|
||||
|
||||
$data = [];
|
||||
$data['start_date'] = $start_date;
|
||||
$data['end_date'] = $end_date;
|
||||
|
||||
foreach ($currencies as $key => $value) {
|
||||
$data[$key]['invoices'] = $this->getInvoiceChartQuery($start_date, $end_date, $key);
|
||||
@ -97,6 +99,9 @@ class ChartService
|
||||
|
||||
$data['currencies'] = $this->getCurrencyCodes();
|
||||
|
||||
$data['start_date'] = $start_date;
|
||||
$data['end_date'] = $end_date;
|
||||
|
||||
$revenue = $this->getRevenue($start_date, $end_date);
|
||||
$outstanding = $this->getOutstanding($start_date, $end_date);
|
||||
$expenses = $this->getExpenses($start_date, $end_date);
|
||||
|
@ -137,7 +137,7 @@ class ApplyPayment
|
||||
->updateBalance($this->amount_applied * -1)
|
||||
->updatePaidToDate($this->amount_applied)
|
||||
->updateStatus()
|
||||
->touchPdf()
|
||||
->deletePdf()
|
||||
->save();
|
||||
|
||||
$this->credit
|
||||
@ -147,7 +147,7 @@ class ApplyPayment
|
||||
event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
if ((int) $this->invoice->balance == 0) {
|
||||
$this->invoice->service()->touchPdf();
|
||||
$this->invoice->service()->deletePdf();
|
||||
$this->invoice = $this->invoice->fresh();
|
||||
event(new InvoiceWasPaid($this->invoice, $this->payment, $this->payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
|
@ -11,15 +11,17 @@
|
||||
|
||||
namespace App\Services\Credit;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Repositories\CreditRepository;
|
||||
use App\Repositories\PaymentRepository;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class CreditService
|
||||
{
|
||||
@ -235,7 +237,24 @@ class CreditService
|
||||
public function deletePdf()
|
||||
{
|
||||
$this->credit->invitations->each(function ($invitation) {
|
||||
(new UnlinkFile(config('filesystems.default'), $this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'))->handle();
|
||||
// (new UnlinkFile(config('filesystems.default'), $this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'))->handle();
|
||||
|
||||
//30-06-2023
|
||||
try {
|
||||
// if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) {
|
||||
Storage::disk(config('filesystems.default'))->delete($this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf');
|
||||
// }
|
||||
|
||||
// if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) {
|
||||
if (Ninja::isHosted()) {
|
||||
Storage::disk('public')->delete($this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
@ -42,7 +42,7 @@ class MarkSent
|
||||
->setStatus(Credit::STATUS_SENT)
|
||||
->applyNumber()
|
||||
->adjustBalance($this->credit->amount)
|
||||
->touchPdf()
|
||||
->deletePdf()
|
||||
->save();
|
||||
|
||||
$this->client
|
||||
|
@ -432,6 +432,7 @@ class Email implements ShouldQueue
|
||||
$this->setGmailMailer();
|
||||
return $this;
|
||||
case 'office365':
|
||||
case 'microsoft':
|
||||
$this->mailer = 'office365';
|
||||
$this->setOfficeMailer();
|
||||
return $this;
|
||||
@ -445,7 +446,8 @@ class Email implements ShouldQueue
|
||||
return $this;
|
||||
|
||||
default:
|
||||
break;
|
||||
$this->mailer = config('mail.default');
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (Ninja::isSelfHost()) {
|
||||
|
@ -168,7 +168,7 @@ class EmailDefaults
|
||||
*/
|
||||
private function setBody(): self
|
||||
{
|
||||
|
||||
|
||||
if (strlen($this->email->email_object->body) > 3) {
|
||||
// A Custom Message has been set in the email screen.
|
||||
// return $this;
|
||||
@ -181,7 +181,7 @@ class EmailDefaults
|
||||
}
|
||||
|
||||
$this->email->email_object->text_body = strip_tags($this->email->email_object->body);
|
||||
|
||||
|
||||
if ($this->template == 'email.template.custom') {
|
||||
$this->email->email_object->body = (str_replace('$body', $this->email->email_object->body, str_replace(["\r","\n"], "", $this->email->email_object->settings->email_style_custom)));
|
||||
}
|
||||
@ -230,7 +230,7 @@ class EmailDefaults
|
||||
|
||||
$this->email->email_object->subject = strtr($this->email->email_object->subject, $this->email->email_object->variables);
|
||||
|
||||
|
||||
|
||||
//06-06-2023 ensure we do not parse markdown in custom templates
|
||||
if ($this->template != 'custom' && $this->template != 'email.template.custom') {
|
||||
$this->email->email_object->body = $this->parseMarkdownToHtml($this->email->email_object->body);
|
||||
@ -303,20 +303,6 @@ class EmailDefaults
|
||||
$this->email->email_object->entity instanceof Quote ||
|
||||
$this->email->email_object->entity instanceof Credit)) {
|
||||
$pdf = ((new CreateRawPdf($this->email->email_object->invitation, $this->email->company->db))->handle());
|
||||
if ($this->email->email_object->settings->enable_e_invoice && $this->email->email_object->entity instanceof Invoice) {
|
||||
|
||||
$xinvoice_path = $this->email->email_object->entity->service()->getEInvoice();
|
||||
|
||||
// $xinvoice_path = (new CreateEInvoice($this->email->email_object->entity, true, stream_get_meta_data($tempfile)['uri']))->handle();
|
||||
// $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]);
|
||||
|
||||
if(Storage::disk(config('filesystems.default'))->exists($xinvoice_path))
|
||||
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode(Storage::get($xinvoice_path)), 'name' => explode(".", $this->email->email_object->entity->getFileName('xml'))[0]."-xinvoice.xml"]]);
|
||||
|
||||
}
|
||||
else {
|
||||
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]);
|
||||
}
|
||||
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]);
|
||||
}
|
||||
|
||||
|
@ -84,15 +84,15 @@ class ApplyPayment extends AbstractService
|
||||
/* Update Pivot Record amount */
|
||||
$this->payment->invoices->each(function ($inv) use ($amount_paid) {
|
||||
if ($inv->id == $this->invoice->id) {
|
||||
$inv->pivot->amount = ($amount_paid * -1);
|
||||
$inv->pivot->save();
|
||||
|
||||
// $inv->pivot->amount = ($amount_paid * -1);
|
||||
// $inv->pivot->save();
|
||||
//25-06-2023
|
||||
$inv->paid_to_date += floatval($amount_paid * -1);
|
||||
$inv->save();
|
||||
}
|
||||
});
|
||||
|
||||
$this->invoice->service()->applyNumber()->workFlow()->touchPdf()->save();
|
||||
$this->invoice->service()->applyNumber()->workFlow()->deletePdf()->save();
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
|
@ -98,7 +98,9 @@ class ZugferdEInvoice extends AbstractService
|
||||
if (!empty($item->notes)){
|
||||
$xrechnung->setDocumentPositionProductDetails($item->product_key, $item->notes);
|
||||
}
|
||||
$xrechnung->setDocumentPositionProductDetails($item->product_key);
|
||||
else {
|
||||
$xrechnung->setDocumentPositionProductDetails($item->product_key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!empty($item->notes)){
|
||||
@ -159,13 +161,7 @@ class ZugferdEInvoice extends AbstractService
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($this->invoice->isPartial()) {
|
||||
$xrechnung->setDocumentSummation($this->invoice->amount, $this->invoice->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount(), $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), null, $this->invoice->partial);
|
||||
} else {
|
||||
$xrechnung->setDocumentSummation($this->invoice->amount, $this->invoice->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount(), $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), null, 0.0);
|
||||
}
|
||||
|
||||
$xrechnung->setDocumentSummation($this->invoice->amount, $this->invoice->balance, $invoicing_data->getSubTotal(), $invoicing_data->getTotalSurcharges(), $invoicing_data->getTotalDiscount(), $invoicing_data->getSubTotal(), $invoicing_data->getItemTotalTaxes(), 0.0, $this->invoice->amount-$this->invoice->balance);
|
||||
|
||||
foreach ($this->tax_map as $item){
|
||||
$xrechnung->addDocumentTax($item["tax_type"], "VAT", $item["net_amount"], $item["tax_rate"]*$item["net_amount"], $item["tax_rate"]*100);
|
||||
@ -236,7 +232,8 @@ class ZugferdEInvoice extends AbstractService
|
||||
}
|
||||
return $tax_type;
|
||||
}
|
||||
private function addtoTaxMap(string $tax_type, float $net_amount, float $tax_rate){
|
||||
private function addtoTaxMap(string $tax_type, float $net_amount, float $tax_rate): void
|
||||
{
|
||||
$hash = hash("md5", $tax_type."-".$tax_rate);
|
||||
if (array_key_exists($hash, $this->tax_map)){
|
||||
$this->tax_map[$hash]["net_amount"] += $net_amount;
|
||||
|
@ -44,9 +44,7 @@ class HandleCancellation extends AbstractService
|
||||
$this->invoice->balance = 0;
|
||||
$this->invoice = $this->invoice->service()->setStatus(Invoice::STATUS_CANCELLED)->save();
|
||||
|
||||
//adjust client balance
|
||||
$this->invoice->client->service()->updateBalance($adjustment)->save();
|
||||
// $this->invoice->fresh();
|
||||
|
||||
$this->invoice->service()->workFlow()->save();
|
||||
|
||||
@ -54,16 +52,6 @@ class HandleCancellation extends AbstractService
|
||||
|
||||
event('eloquent.updated: App\Models\Invoice', $this->invoice);
|
||||
|
||||
$transaction = [
|
||||
'invoice' => $this->invoice->transaction_event(),
|
||||
'payment' => [],
|
||||
'client' => $this->invoice->client->transaction_event(),
|
||||
'credit' => [],
|
||||
'metadata' => [],
|
||||
];
|
||||
|
||||
// TransactionLog::dispatch(TransactionEvent::INVOICE_CANCELLED, $transaction, $this->invoice->company->db);
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,6 @@ class InvoiceService
|
||||
*/
|
||||
public function applyPayment(Payment $payment, float $payment_amount)
|
||||
{
|
||||
// $this->deletePdf();
|
||||
$this->invoice = $this->markSent()->save();
|
||||
|
||||
$this->invoice = (new ApplyPayment($this->invoice, $payment, $payment_amount))->run();
|
||||
@ -339,7 +338,7 @@ class InvoiceService
|
||||
return $item;
|
||||
})->toArray();
|
||||
|
||||
$this->touchPdf();
|
||||
$this->deletePdf();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -348,13 +347,15 @@ class InvoiceService
|
||||
{
|
||||
$this->invoice->load('invitations');
|
||||
|
||||
//30-06-2023
|
||||
$this->invoice->invitations->each(function ($invitation) {
|
||||
try {
|
||||
if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) {
|
||||
// if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) {
|
||||
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf');
|
||||
}
|
||||
// }
|
||||
|
||||
if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) {
|
||||
// if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) {
|
||||
if (Ninja::isHosted()) {
|
||||
Storage::disk('public')->delete($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
@ -371,11 +372,12 @@ class InvoiceService
|
||||
|
||||
$this->invoice->invitations->each(function ($invitation) {
|
||||
try {
|
||||
if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) {
|
||||
// if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) {
|
||||
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"));
|
||||
}
|
||||
// }
|
||||
|
||||
if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) {
|
||||
// if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) {
|
||||
if (Ninja::isHosted()) {
|
||||
Storage::disk('public')->delete($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
@ -403,7 +405,7 @@ class InvoiceService
|
||||
})->toArray();
|
||||
|
||||
$this->invoice = $this->invoice->calc()->getInvoice();
|
||||
$this->touchPdf();
|
||||
$this->deletePdf();
|
||||
|
||||
/* 24-03-2022 */
|
||||
$new_balance = $this->invoice->balance;
|
||||
|
@ -102,7 +102,7 @@ class MarkPaid extends AbstractService
|
||||
$this->invoice
|
||||
->service()
|
||||
->applyNumber()
|
||||
->touchPdf()
|
||||
->deletePdf()
|
||||
->save();
|
||||
|
||||
$payment->ledger()
|
||||
|
@ -54,7 +54,7 @@ class TriggeredActions extends AbstractService
|
||||
}
|
||||
|
||||
if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') {
|
||||
$this->invoice->service()->markSent()->touchPdf()->save();
|
||||
$this->invoice->service()->markSent()->save();
|
||||
$this->sendEmail();
|
||||
$this->updated = false;
|
||||
}
|
||||
|
@ -33,14 +33,7 @@ class SendEmail
|
||||
$this->contact = $this->payment->client->contacts()->first();
|
||||
}
|
||||
|
||||
// $this->payment->invoices->sortByDesc('id')->first(function ($invoice) {
|
||||
// $invoice->invitations->each(function ($invitation) {
|
||||
// if (!$invitation->contact->trashed() && $invitation->contact->email) {
|
||||
EmailPayment::dispatch($this->payment, $this->payment->company, $this->contact);
|
||||
|
||||
// event(new PaymentWasEmailed($this->payment, $this->payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class UpdateInvoicePayment
|
||||
$invoice = $invoice->service()
|
||||
->clearPartial()
|
||||
->updateStatus()
|
||||
->touchPdf()
|
||||
->deletePdf()
|
||||
->workFlow()
|
||||
->save();
|
||||
|
||||
|
@ -653,7 +653,7 @@ class PdfBuilder
|
||||
$data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->service->config->currency_entity) : '';
|
||||
|
||||
if ($item->quantity > 0 || $item->cost > 0) {
|
||||
$data[$key][$table_type.'.quantity'] = $this->service->config->formatMoney($item->quantity);
|
||||
$data[$key][$table_type.'.quantity'] = $item->quantity;
|
||||
|
||||
$data[$key][$table_type.'.unit_cost'] = $this->service->config->formatMoney($item->cost);
|
||||
|
||||
|
@ -232,6 +232,9 @@ class PdfMock
|
||||
'$secondary_font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
|
||||
'$contact.signature' => '',
|
||||
'$company_logo_size' => $this->settings->company_logo_size ?: '65%',
|
||||
'$product.tax_rate1' => ctrans('texts.tax'),
|
||||
'$product.tax_rate2' => ctrans('texts.tax'),
|
||||
'$product.tax_rate3' => ctrans('texts.tax'),
|
||||
'$product.tax_name1' => '',
|
||||
'$product.tax_name2' => '',
|
||||
'$product.tax_name3' => '',
|
||||
@ -688,8 +691,11 @@ class PdfMock
|
||||
'$net_subtotal_label' => ctrans('texts.net_subtotal'),
|
||||
'$credit.total_label' => ctrans('texts.total'),
|
||||
'$quote.amount_label' => ctrans('texts.amount'),
|
||||
'$description_label' => ctrans('texts.description'),
|
||||
'$product.tax_rate1_label' => ctrans('texts.tax'),
|
||||
'$product.tax_rate2_label' => ctrans('texts.tax'),
|
||||
'$product.tax_rate3_label' => ctrans('texts.tax'),
|
||||
'$product.tax_label' => ctrans('texts.tax'),
|
||||
'$description_label' => ctrans('texts.description'),
|
||||
'$your_entity_label' => ctrans("texts.your_{$this->entity_string}"),
|
||||
'$view_button_label' => ctrans('texts.view'),
|
||||
'$status_logo_label' => ctrans('texts.logo'),
|
||||
@ -782,9 +788,9 @@ class PdfMock
|
||||
'$amount_label' => ctrans('texts.amount'),
|
||||
'$notes_label' => ctrans('texts.notes'),
|
||||
'$terms_label' => ctrans('texts.terms'),
|
||||
'tax_rate1_label' => ctrans('texts.tax_rate1'),
|
||||
'tax_rate2_label' => ctrans('texts.tax_rate2'),
|
||||
'tax_rate3_label' => ctrans('texts.tax_rate3'),
|
||||
'$tax_rate1_label' => ctrans('texts.tax_rate1'),
|
||||
'$tax_rate2_label' => ctrans('texts.tax_rate2'),
|
||||
'$tax_rate3_label' => ctrans('texts.tax_rate3'),
|
||||
'$phone_label' => ctrans('texts.phone'),
|
||||
'$email_label' => ctrans('texts.email'),
|
||||
'$taxes_label' => ctrans('texts.taxes'),
|
||||
|
@ -19,11 +19,8 @@ use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class GetPurchaseOrderPdf extends AbstractService
|
||||
{
|
||||
public function __construct(PurchaseOrder $purchase_order, VendorContact $contact = null)
|
||||
public function __construct(public PurchaseOrder $purchase_order, public ?VendorContact $contact = null)
|
||||
{
|
||||
$this->purchase_order = $purchase_order;
|
||||
|
||||
$this->contact = $contact;
|
||||
}
|
||||
|
||||
public function run()
|
||||
|
@ -43,10 +43,6 @@ class TriggeredActions extends AbstractService
|
||||
$this->purchase_order = $this->purchase_order->service()->markSent()->touchPdf()->save();
|
||||
}
|
||||
|
||||
// if ($this->request->has('cancel') && $this->request->input('cancel') == 'true') {
|
||||
// $this->purchase_order = $this->purchase_order->service()->handleCancellation()->save();
|
||||
// }
|
||||
|
||||
if ($this->request->has('save_default_footer') && $this->request->input('save_default_footer') == 'true') {
|
||||
$company = $this->purchase_order->company;
|
||||
$settings = $company->settings;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user