Merge pull request #7280 from turbo124/v5-stable

v5.3.67
This commit is contained in:
David Bomba 2022-03-12 19:56:09 +11:00 committed by GitHub
commit bb5072d58f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
141 changed files with 453026 additions and 448025 deletions

View File

@ -1 +1 @@
5.3.66 5.3.67

View File

@ -74,7 +74,7 @@ class CheckData extends Command
/** /**
* @var string * @var string
*/ */
protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=}'; protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=}';
/** /**
* @var string * @var string
@ -102,7 +102,7 @@ class CheckData extends Command
config(['database.default' => $database]); config(['database.default' => $database]);
} }
// $this->checkInvoiceBalances(); $this->checkInvoiceBalances();
$this->checkInvoiceBalancesNew(); $this->checkInvoiceBalancesNew();
//$this->checkInvoicePayments(); //$this->checkInvoicePayments();
@ -482,6 +482,7 @@ class CheckData extends Command
payments.id = paymentables.payment_id payments.id = paymentables.payment_id
WHERE paymentable_type = ? WHERE paymentable_type = ?
AND paymentables.deleted_at is NULL AND paymentables.deleted_at is NULL
AND payments.amount > 0
AND payments.is_deleted = 0 AND payments.is_deleted = 0
AND payments.client_id = ?; AND payments.client_id = ?;
"), [App\Models\Credit::class, $client->id] ); "), [App\Models\Credit::class, $client->id] );
@ -499,9 +500,11 @@ class CheckData extends Command
{ {
$client = Client::withTrashed()->find($_client->client_id); $client = Client::withTrashed()->find($_client->client_id);
$credits_from_reversal = Credit::withTrashed()->where('client_id', $client->id)->where('is_deleted', 0)->whereNotNull('invoice_id')->sum('amount');
$credits_used_for_payments = $this->clientCreditPaymentables($client); $credits_used_for_payments = $this->clientCreditPaymentables($client);
$total_paid_to_date = $_client->payments_applied + $credits_used_for_payments[0]->credit_payment; $total_paid_to_date = $_client->payments_applied + $credits_used_for_payments[0]->credit_payment - $credits_from_reversal;
if(round($total_paid_to_date,2) != round($_client->client_paid_to_date,2)){ if(round($total_paid_to_date,2) != round($_client->client_paid_to_date,2)){
@ -736,7 +739,7 @@ ORDER BY clients.id;
$this->logMessage($client_object->present()->name.' - '.$client_object->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client['client_balance'], '0'). " Ledger balance = {$ledger->balance}"); $this->logMessage($client_object->present()->name.' - '.$client_object->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client['client_balance'], '0'). " Ledger balance = {$ledger->balance}");
if($this->option('client_balance')){ if($this->option('ledger_balance')){
$this->logMessage("# {$client_object->id} " . $client_object->present()->name.' - '.$client_object->number." Fixing {$client_object->balance} to {$invoice_balance}"); $this->logMessage("# {$client_object->id} " . $client_object->present()->name.' - '.$client_object->number." Fixing {$client_object->balance} to {$invoice_balance}");
$client_object->balance = $invoice_balance; $client_object->balance = $invoice_balance;
@ -853,18 +856,16 @@ ORDER BY clients.id;
foreach (Client::where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2))->cursor() as $client) { foreach (Client::where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2))->cursor() as $client) {
$invoice_balance = $client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance'); $invoice_balance = $client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$credit_balance = $client->credits()->where('is_deleted', false)->sum('balance');
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first(); $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
if ($ledger && number_format($invoice_balance, 4) != number_format($client->balance, 4)) { if ($ledger && number_format($ledger->balance, 4) != number_format($client->balance, 4)) {
$this->wrong_balances++; $this->wrong_balances++;
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}"); $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
$this->isValid = false; $this->isValid = false;
if($this->option('client_balance')){ if($this->option('ledger_balance')){
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}"); $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
$client->balance = $invoice_balance; $client->balance = $invoice_balance;
@ -879,7 +880,7 @@ ORDER BY clients.id;
} }
} }
$this->logMessage("{$this->wrong_balances} clients with incorrect balances"); $this->logMessage("{$this->wrong_balances} clients with incorrect ledger balances");
} }
private function checkLogoFiles() private function checkLogoFiles()
@ -1011,6 +1012,27 @@ ORDER BY clients.id;
} }
/* //used to set a company owner on the company_users table
$c = Company::whereDoesntHave('company_users', function ($query){
$query->where('is_owner', true)->withTrashed();
})->cursor()->each(function ($company){
if(!$company->company_users()->exists()){
echo "No company users AT ALL {$company->id}\n";
}
else{
$cu = $company->company_users()->orderBy('id', 'ASC')->orderBy('is_admin', 'ASC')->first();
echo "{$company->id} - {$cu->id} \n";
$cu->is_owner=true;
$cu->save();
}
});
*/
/* query if we want to company company ledger to client balance /* query if we want to company company ledger to client balance
$results = \DB::select( \DB::raw(" $results = \DB::select( \DB::raw("
SELECT SELECT

View File

@ -11,6 +11,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\DataMapper\ClientRegistrationFields;
use App\DataMapper\CompanySettings; use App\DataMapper\CompanySettings;
use App\DataMapper\FeesAndLimits; use App\DataMapper\FeesAndLimits;
use App\Events\Invoice\InvoiceWasCreated; use App\Events\Invoice\InvoiceWasCreated;
@ -115,6 +116,7 @@ class CreateSingleAccount extends Command
$settings->invoice_footer = 'Default invoice footer'; $settings->invoice_footer = 'Default invoice footer';
$company->settings = $settings; $company->settings = $settings;
$company->client_registration_fields = ClientRegistrationFields::generate();
$company->save(); $company->save();
$account->default_company_id = $company->id; $account->default_company_id = $company->id;

View File

@ -64,7 +64,7 @@ class Kernel extends ConsoleKernel
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping(); $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping();
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping(); $schedule->job(new AutoBillCron)->dailyAt('12:20')->withoutOverlapping();
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping(); $schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();

View File

@ -0,0 +1,51 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class TrialFinished extends GenericCounter
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'counter';
/**
* The name of the counter.
* @var string
*/
public $name = 'account.trial_finished';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The increment amount... should always be
* set to 0.
*
* @var int
*/
public $metric = 0;
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class TrialStarted extends GenericCounter
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'counter';
/**
* The name of the counter.
* @var string
*/
public $name = 'account.trial_started';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The increment amount... should always be
* set to 0.
*
* @var int
*/
public $metric = 0;
}

View File

@ -66,7 +66,7 @@ class CompanySettings extends BaseSettings
public $auto_convert_quote = true; //@implemented public $auto_convert_quote = true; //@implemented
public $auto_email_invoice = true; //@only used for Recurring Invoices, if set to false, we never send? public $auto_email_invoice = true; //@only used for Recurring Invoices, if set to false, we never send?
public $entity_send_time = 0; public $entity_send_time = 6;
public $inclusive_taxes = false; //@implemented public $inclusive_taxes = false; //@implemented
public $quote_footer = ''; //@implmented public $quote_footer = ''; //@implmented

View File

@ -123,7 +123,7 @@ class EmailTemplateDefaults
public static function emailInvoiceTemplate() public static function emailInvoiceTemplate()
{ {
$invoice_message = '<p>'.self::transformText('invoice_message').'</p><div class="center">$view_button</div>'; $invoice_message = '<p>$client<br><br>'.self::transformText('invoice_message').'</p><div class="center">$view_button</div>';
return $invoice_message; return $invoice_message;
} }
@ -135,7 +135,7 @@ class EmailTemplateDefaults
public static function emailQuoteTemplate() public static function emailQuoteTemplate()
{ {
$quote_message = '<p>'.self::transformText('quote_message').'</p><div class="center">$view_button</div>'; $quote_message = '<p>$client<br><br>'.self::transformText('quote_message').'</p><div class="center">$view_button</div>';
return $quote_message; return $quote_message;
} }
@ -147,21 +147,21 @@ class EmailTemplateDefaults
public static function emailPaymentTemplate() public static function emailPaymentTemplate()
{ {
$payment_message = '<p>'.self::transformText('payment_message').'</p><div class="center">$view_button</div>'; $payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div class="center">$view_button</div>';
return $payment_message; return $payment_message;
} }
public static function emailCreditTemplate() public static function emailCreditTemplate()
{ {
$credit_message = '<p>'.self::transformText('credit_message').'</p><div class="center">$view_button</div>'; $credit_message = '<p>$client<br><br>'.self::transformText('credit_message').'</p><div class="center">$view_button</div>';
return $credit_message; return $credit_message;
} }
public static function emailPaymentPartialTemplate() public static function emailPaymentPartialTemplate()
{ {
$payment_message = '<p>'.self::transformText('payment_message').'</p><div class="center">$view_button</div>'; $payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div class="center">$view_button</div>';
return $payment_message; return $payment_message;
} }

View File

@ -0,0 +1,100 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* BaseTransaction.
*/
class BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_MARK_PAID;
public array $model = [
'client_id',
'invoice_id',
'payment_id',
'credit_id',
'client_balance',
'client_paid_to_date',
'client_credit_balance',
'invoice_balance',
'invoice_amount',
'invoice_partial',
'invoice_paid_to_date',
'invoice_status',
'payment_amount',
'payment_applied',
'payment_refunded',
'payment_status',
'paymentables',
'event_id',
'timestamp',
'payment_request',
'metadata',
'credit_balance',
'credit_amount',
'credit_status',
];
public function transform(array $data) :array
{
// $invoice = $data['invoice'];
// $payment = $data['payment'];
// $client = $data['client'];
// $credit = $data['credit'];
// $payment_request = $data['payment']['payment_request'];
// $metadata = $data['metadata'];
return array_merge(
$data['invoice'],
$data['payment'],
$data['client'],
$data['credit'],
$data['metadata'],
['event_id' => $this->event_id, 'timestamp' =>time()],
);
// return [
// 'event_id' => $this->event_id,
// 'client_id' => $client?->id,
// 'invoice_id' => $invoice?->id,
// 'payment_id' => $payment?->id,
// 'credit_id' => $credit?->creditid,
// 'client_balance' => $client?->balance,
// 'client_paid_to_date' => $client?->paid_to_date,
// 'client_credit_balance' => $client?->credit_balance,
// 'invoice_balance' => $invoice?->balance,
// 'invoice_amount' => $invoice?->amount,
// 'invoice_partial' => $invoice?->partial,
// 'invoice_paid_to_date' => $invoice?->paid_to_date,
// 'invoice_status' => $invoice?->status_id,
// 'payment_amount' => $payment?->amount,
// 'payment_applied' => $payment?->applied,
// 'payment_refunded' => $payment?->refunded,
// 'payment_status' => $payment?->status_id,
// 'paymentables' => $payment?->paymentables,
// 'payment_request' => $payment_request,
// 'metadata' => $metadata,
// 'credit_balance' => $credit?->balance,
// 'credit_amount' => $credit?->amount,
// 'credit_status' => $credit?->status_id,
// 'timestamp' => time(),
// ];
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* ClientStatusTransaction.
*/
class ClientStatusTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::CLIENT_STATUS;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* GatewayPaymentMadeTransaction.
*/
class GatewayPaymentMadeTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::GATEWAY_PAYMENT_MADE;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* InvoiceCancelledTransaction.
*/
class InvoiceCancelledTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_CANCELLED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* InvoiceDeletedTransaction.
*/
class InvoiceDeletedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_DELETED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* InvoiceFeeAppliedTransaction.
*/
class InvoiceFeeAppliedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_FEE_APPLIED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* InvoicePaymentTransaction.
*/
class InvoicePaymentTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_PAYMENT_APPLIED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* InvoiceReversalTransaction.
*/
class InvoiceReversalTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_REVERSED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* InvoiceUpdatedTransaction.
*/
class InvoiceUpdatedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_UPDATED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* BaseTransaction.
*/
class MarkPaidTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::INVOICE_MARK_PAID;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* PaymentAppliedTransaction.
*/
class PaymentAppliedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::PAYMENT_APPLIED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* PaymentDeletedTransaction.
*/
class PaymentDeletedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::PAYMENT_DELETED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* PaymentFailedTransaction.
*/
class PaymentFailedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::PAYMENT_FAILED;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* PaymentMadeTransaction.
*/
class PaymentMadeTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::PAYMENT_MADE;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\TransactionEvent;
/**
* PaymentRefundedTransaction.
*/
class PaymentRefundedTransaction extends BaseTransaction implements TransactionInterface
{
public $event_id = TransactionEvent::PAYMENT_REFUND;
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Transactions;
interface TransactionInterface
{
public function transform(array $data) :array;
}

View File

@ -28,7 +28,7 @@ class CreditFactory
$credit->terms = ''; $credit->terms = '';
$credit->public_notes = ''; $credit->public_notes = '';
$credit->private_notes = ''; $credit->private_notes = '';
$credit->date = null; $credit->date = now()->format('Y-m-d');
$credit->due_date = null; $credit->due_date = null;
$credit->partial_due_date = null; $credit->partial_due_date = null;
$credit->is_deleted = false; $credit->is_deleted = false;

View File

@ -27,7 +27,7 @@ class InvoiceFactory
$invoice->terms = ''; $invoice->terms = '';
$invoice->public_notes = ''; $invoice->public_notes = '';
$invoice->private_notes = ''; $invoice->private_notes = '';
$invoice->date = null; $invoice->date = now()->format('Y-m-d');
$invoice->due_date = null; $invoice->due_date = null;
$invoice->partial_due_date = null; $invoice->partial_due_date = null;
$invoice->is_deleted = false; $invoice->is_deleted = false;

View File

@ -27,7 +27,7 @@ class QuoteFactory
$quote->terms = ''; $quote->terms = '';
$quote->public_notes = ''; $quote->public_notes = '';
$quote->private_notes = ''; $quote->private_notes = '';
$quote->date = null; $quote->date = now()->format('Y-m-d');
$quote->due_date = null; $quote->due_date = null;
$quote->partial_due_date = null; $quote->partial_due_date = null;
$quote->is_deleted = false; $quote->is_deleted = false;

View File

@ -89,9 +89,6 @@ class InvoiceItemSumInclusive
{ {
$this->setLineTotal($this->item->cost * $this->item->quantity); $this->setLineTotal($this->item->cost * $this->item->quantity);
//11-02-2022
// $this->setLineTotal($this->formatValue($this->item->cost, $this->currency->precision) * $this->formatValue($this->item->quantity, $this->currency->precision));
return $this; return $this;
} }
@ -171,6 +168,8 @@ class InvoiceItemSumInclusive
public function setLineTotal($total) public function setLineTotal($total)
{ {
$this->item->gross_line_total = $total;
$this->item->line_total = $total; $this->item->line_total = $total;
return $this; return $this;

View File

@ -764,7 +764,8 @@ class BaseController extends Controller
$this->buildCache(); $this->buildCache();
return view('index.index', $data); return response()->view('index.index', $data)->header('X-Frame-Options', 'SAMEORIGIN', false);
} }
return redirect('/setup'); return redirect('/setup');

View File

@ -264,17 +264,17 @@ class InvitationController extends Controller
abort(404, "Invoice not found"); abort(404, "Invoice not found");
} }
public function unsubscribe(Request $request, string $entity_type, string $invitation_key) public function unsubscribe(Request $request, string $entity, string $invitation_key)
{ {
if($entity_type == 'invoice'){ if($entity == 'invoice'){
$invite = InvoiceInvitation::withTrashed()->where('key', $invitation_key)->first(); $invite = InvoiceInvitation::withTrashed()->where('key', $invitation_key)->first();
$invite->contact->send_email = false; $invite->contact->send_email = false;
$invite->contact->save(); $invite->contact->save();
}elseif($entity_type == 'quote'){ }elseif($entity == 'quote'){
$invite = QuoteInvitation::withTrashed()->where('key', $invitation_key)->first(); $invite = QuoteInvitation::withTrashed()->where('key', $invitation_key)->first();
$invite->contact->send_email = false; $invite->contact->send_email = false;
$invite->contact->save(); $invite->contact->save();
}elseif($entity_type == 'credit'){ }elseif($entity == 'credit'){
$invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first(); $invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first();
$invite->contact->send_email = false; $invite->contact->send_email = false;
$invite->contact->save(); $invite->contact->save();

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers\ClientPortal; namespace App\Http\Controllers\ClientPortal;
use App\DataMapper\Analytics\TrialStarted;
use App\Factory\RecurringInvoiceFactory; use App\Factory\RecurringInvoiceFactory;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\ClientPortal\Uploads\StoreUploadRequest; use App\Http\Requests\ClientPortal\Uploads\StoreUploadRequest;
@ -32,6 +33,7 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Turbo124\Beacon\Facades\LightLogs;
class NinjaPlanController extends Controller class NinjaPlanController extends Controller
{ {
@ -134,7 +136,7 @@ class NinjaPlanController extends Controller
// $account = auth()->guard('contact')->user()->company->account; // $account = auth()->guard('contact')->user()->company->account;
if(auth()->guard('contact')->user()->client->custom_value2){ if(auth()->guard('contact')->user()->client->custom_value2){
MultiDB::findAndSetDbByAccountKey(auth()->guard('contact')->user()->client->custom_value2); MultiDB::findAndSetDbByAccountKey(auth()->guard('contact')->user()->client->custom_value2);
$account = Account::where('key', auth()->guard('contact')->user()->client->custom_value2); $account = Account::where('key', auth()->guard('contact')->user()->client->custom_value2)->first();
$account->trial_started = now(); $account->trial_started = now();
$account->trial_plan = 'pro'; $account->trial_plan = 'pro';
$account->save(); $account->save();
@ -159,11 +161,15 @@ class NinjaPlanController extends Controller
$recurring_invoice->next_send_date = now()->addDays(14)->format('Y-m-d'); $recurring_invoice->next_send_date = now()->addDays(14)->format('Y-m-d');
$recurring_invoice->save(); $recurring_invoice->save();
$recurring_invoice = $recurring_invoice->calc()->getRecurringInvoice(); $r = $recurring_invoice->calc()->getRecurringInvoice();
$recurring_invoice->service()->start(); $recurring_invoice->service()->start()->save();
return redirect('/'); LightLogs::create(new TrialStarted())
->increment()
->queue();
return $this->render('plan.trial_confirmed', $data);
} }
@ -180,7 +186,7 @@ class NinjaPlanController extends Controller
public function plan() public function plan()
{ {
// return $this->trial();
//harvest the current plan //harvest the current plan
$data = []; $data = [];
$data['late_invoice'] = false; $data['late_invoice'] = false;

View File

@ -143,7 +143,7 @@ class PaymentMethodController extends Controller
return redirect() return redirect()
->route('client.payment_methods.index') ->route('client.payment_methods.index')
->withSuccess('Payment method has been successfully removed.'); ->withSuccess(ctrans('texts.payment_method_removed'));
} }
private function getClientGateway() private function getClientGateway()

View File

@ -29,11 +29,13 @@ use App\Http\Requests\Invoice\UploadInvoiceRequest;
use App\Jobs\Entity\EmailEntity; use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\StoreInvoice; use App\Jobs\Invoice\StoreInvoice;
use App\Jobs\Invoice\ZipInvoices; use App\Jobs\Invoice\ZipInvoices;
use App\Jobs\Ninja\TransactionLog;
use App\Jobs\Util\UnlinkFile; use App\Jobs\Util\UnlinkFile;
use App\Models\Account; use App\Models\Account;
use App\Models\Client; use App\Models\Client;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Quote; use App\Models\Quote;
use App\Models\TransactionEvent;
use App\Repositories\InvoiceRepository; use App\Repositories\InvoiceRepository;
use App\Transformers\InvoiceTransformer; use App\Transformers\InvoiceTransformer;
use App\Transformers\QuoteTransformer; use App\Transformers\QuoteTransformer;
@ -224,6 +226,16 @@ class InvoiceController extends BaseController
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$transaction = [
'invoice' => $invoice->transaction_event(),
'payment' => [],
'client' => $invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
return $this->itemResponse($invoice); return $this->itemResponse($invoice);
} }
@ -405,6 +417,16 @@ class InvoiceController extends BaseController
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$transaction = [
'invoice' => $invoice->transaction_event(),
'payment' => [],
'client' => $invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
return $this->itemResponse($invoice); return $this->itemResponse($invoice);
} }
@ -663,7 +685,7 @@ class InvoiceController extends BaseController
return $this->errorResponse(['message' => ctrans('texts.invoice_cannot_be_marked_paid')], 400); return $this->errorResponse(['message' => ctrans('texts.invoice_cannot_be_marked_paid')], 400);
} }
$invoice = $invoice->service()->markPaid(); $invoice = $invoice->service()->markPaid()->save();
if (! $bulk) { if (! $bulk) {
return $this->itemResponse($invoice); return $this->itemResponse($invoice);
@ -935,6 +957,9 @@ class InvoiceController extends BaseController
if ($request->has('documents')) if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $invoice); $this->saveDocuments($request->file('documents'), $invoice);
if ($request->has('file'))
$this->saveDocuments($request->file('documents'), $invoice);
return $this->itemResponse($invoice->fresh()); return $this->itemResponse($invoice->fresh());
} }

View File

@ -672,8 +672,7 @@ class QuoteController extends BaseController
return $this->itemResponse($quote); return $this->itemResponse($quote);
break; break;
case 'approve': case 'approve':
//make sure it hasn't already been approved!! if (!in_array($quote->status_id, [Quote::STATUS_SENT, Quote::STATUS_DRAFT]) ) {
if ($quote->status_id != Quote::STATUS_SENT) {
return response()->json(['message' => ctrans('texts.quote_unapprovable')], 400); return response()->json(['message' => ctrans('texts.quote_unapprovable')], 400);
} }

View File

@ -28,6 +28,8 @@ class RequiredClientInfo extends Component
*/ */
public $contact; public $contact;
public $client;
/** /**
* @var array * @var array
*/ */
@ -64,6 +66,36 @@ class RequiredClientInfo extends Component
// 'contact_phone' => 'phone', // 'contact_phone' => 'phone',
]; ];
public $client_address_array = [
'address1',
'address2',
'city',
'state',
'postal_code',
'country_id',
];
protected $rules = [
'client.address1' => '',
'client.address2' => '',
'client.city' => '',
'client.state' => '',
'client.postal_code' => '',
'client.country_id' => '',
'client.shipping_address1' => '',
'client.shipping_address2' => '',
'client.shipping_city' => '',
'client.shipping_state' => '',
'client.shipping_postal_code' => '',
'client.shipping_country_id' => '',
'contact.first_name' => '',
'contact.last_name' => '',
'contact.email' => '',
'client.name' => '',
'client.website' => '',
'client.phone' => '',
];
public $show_form = false; public $show_form = false;
public $company; public $company;
@ -71,6 +103,13 @@ class RequiredClientInfo extends Component
public function mount() public function mount()
{ {
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
$this->client = $this->contact->client;
count($this->fields) > 0
? $this->checkFields()
: $this->show_form = false;
} }
public function handleSubmit(array $data): bool public function handleSubmit(array $data): bool
@ -141,8 +180,7 @@ class RequiredClientInfo extends Component
$_field = $this->mappings[$field['name']]; $_field = $this->mappings[$field['name']];
if (Str::startsWith($field['name'], 'client_')) { if (Str::startsWith($field['name'], 'client_')) {
if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field})) { if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || in_array($_field, $this->client_address_array)) {
// if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} == 840) {
$this->show_form = true; $this->show_form = true;
} else { } else {
$this->fields[$index]['filled'] = true; $this->fields[$index]['filled'] = true;
@ -151,7 +189,6 @@ class RequiredClientInfo extends Component
if (Str::startsWith($field['name'], 'contact_')) { if (Str::startsWith($field['name'], 'contact_')) {
if (empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) { if (empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) {
// if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} == 840) {
$this->show_form = true; $this->show_form = true;
} else { } else {
$this->fields[$index]['filled'] = true; $this->fields[$index]['filled'] = true;
@ -193,10 +230,6 @@ class RequiredClientInfo extends Component
public function render() public function render()
{ {
count($this->fields) > 0
? $this->checkFields()
: $this->show_form = false;
return render('components.livewire.required-client-info'); return render('components.livewire.required-client-info');
} }
} }

View File

@ -29,7 +29,15 @@ class TrustProxies extends Middleware
* *
* @var int * @var int
*/ */
protected $headers = Request::HEADER_X_FORWARDED_ALL; // protected $headers = Request::HEADER_X_FORWARDED_ALL;
//07-03-2022 - fixes for symfony 5.2
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
/* /*
* Instantiate trusted proxies middleware * Instantiate trusted proxies middleware

View File

@ -76,7 +76,7 @@ class StoreClientRequest extends Request
]; ];
if (auth()->user()->company()->account->isFreeHostedClient()) { if (auth()->user()->company()->account->isFreeHostedClient()) {
$rules['hosted_clients'] = new CanStoreClientsRule($this->company_id); $rules['id'] = new CanStoreClientsRule(auth()->user()->company()->id);
} }
$rules['number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)]; $rules['number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];

View File

@ -47,6 +47,16 @@ class StoreInvoiceRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'; $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
} }
if ($this->input('file') && is_array($this->input('file'))) {
$documents = count($this->input('file'));
foreach (range(0, $documents) as $index) {
$rules['file.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
} elseif ($this->input('file')) {
$rules['file'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
// $rules['client_id'] = ['required', Rule::exists('clients')->where('company_id', auth()->user()->company()->id)]; // $rules['client_id'] = ['required', Rule::exists('clients')->where('company_id', auth()->user()->company()->id)];

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\Invoice;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Http\ValidationRules\Invoice\InvoiceBalanceSanity; use App\Http\ValidationRules\Invoice\InvoiceBalanceSanity;
use App\Http\ValidationRules\Invoice\LockedInvoiceRule; use App\Http\ValidationRules\Invoice\LockedInvoiceRule;
use App\Http\ValidationRules\Project\ValidProjectForClient;
use App\Models\Invoice; use App\Models\Invoice;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
@ -59,6 +60,7 @@ class UpdateInvoiceRequest extends Request
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric'; $rules['discount'] = 'sometimes|numeric';
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
// if($this->input('status_id') != Invoice::STATUS_DRAFT) // if($this->input('status_id') != Invoice::STATUS_DRAFT)
// $rules['balance'] = new InvoiceBalanceSanity($this->invoice, $this->all()); // $rules['balance'] = new InvoiceBalanceSanity($this->invoice, $this->all());

View File

@ -33,6 +33,9 @@ class UploadInvoiceRequest extends Request
if($this->input('documents')) if($this->input('documents'))
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000'; $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
if($this->input('file'))
$rules['file'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules; return $rules;
} }

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\RecurringInvoice; namespace App\Http\Requests\RecurringInvoice;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Http\ValidationRules\Project\ValidProjectForClient;
use App\Http\ValidationRules\Recurring\UniqueRecurringInvoiceNumberRule; use App\Http\ValidationRules\Recurring\UniqueRecurringInvoiceNumberRule;
use App\Models\Client; use App\Models\Client;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
@ -55,6 +56,8 @@ class StoreRecurringInvoiceRequest extends Request
$rules['frequency_id'] = 'required|integer|digits_between:1,12'; $rules['frequency_id'] = 'required|integer|digits_between:1,12';
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
$rules['number'] = new UniqueRecurringInvoiceNumberRule($this->all()); $rules['number'] = new UniqueRecurringInvoiceNumberRule($this->all());
return $rules; return $rules;
@ -80,6 +83,11 @@ class StoreRecurringInvoiceRequest extends Request
$input['vendor_id'] = $this->decodePrimaryKey($input['vendor_id']); $input['vendor_id'] = $this->decodePrimaryKey($input['vendor_id']);
} }
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
}
if (isset($input['client_contacts'])) { if (isset($input['client_contacts'])) {
foreach ($input['client_contacts'] as $key => $contact) { foreach ($input['client_contacts'] as $key => $contact) {
if (! array_key_exists('send_email', $contact) || ! array_key_exists('id', $contact)) { if (! array_key_exists('send_email', $contact) || ! array_key_exists('id', $contact)) {

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\RecurringInvoice; namespace App\Http\Requests\RecurringInvoice;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Http\ValidationRules\Project\ValidProjectForClient;
use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -51,6 +52,7 @@ class UpdateRecurringInvoiceRequest extends Request
if($this->number) if($this->number)
$rules['number'] = Rule::unique('recurring_invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_invoice->id); $rules['number'] = Rule::unique('recurring_invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_invoice->id);
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
return $rules; return $rules;
} }
@ -59,16 +61,6 @@ class UpdateRecurringInvoiceRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
// foreach($this->input('documents') as $document)
// {
// if($document instanceof UploadedFile){
// nlog("i am an uploaded file");
// nlog($document);
// }
// else
// nlog($document);
// }
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']); $input['design_id'] = $this->decodePrimaryKey($input['design_id']);
} }
@ -81,6 +73,11 @@ class UpdateRecurringInvoiceRequest extends Request
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']); $input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
} }
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
}
if (isset($input['invitations'])) { if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) { foreach ($input['invitations'] as $key => $value) {
if (is_numeric($input['invitations'][$key]['id'])) { if (is_numeric($input['invitations'][$key]['id'])) {

View File

@ -36,14 +36,19 @@ class StoreVendorRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/ /* Ensure we have a client name, and that all emails are unique*/
//$rules['name'] = 'required|min:1'; //$rules['name'] = 'required|min:1';
$rules['id_number'] = 'unique:vendors,id_number,'.$this->id.',id,company_id,'.$this->company_id; // $rules['id_number'] = 'unique:vendors,id_number,'.$this->id.',id,company_id,'.auth()->user()->company()->id;
//$rules['settings'] = new ValidVendorGroupSettingsRule(); //$rules['settings'] = new ValidVendorGroupSettingsRule();
$rules['contacts.*.email'] = 'nullable|distinct';
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
if (isset($this->number)) { if (isset($this->number)) {
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id); $rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id);
} }
// if (isset($this->id_number)) {
// $rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id);
// }
return $rules; return $rules;
} }
@ -59,7 +64,7 @@ class StoreVendorRequest extends Request
public function messages() public function messages()
{ {
return [ return [
'unique' => ctrans('validation.unique', ['attribute' => 'email']), // 'unique' => ctrans('validation.unique', ['attribute' => 'email']),
//'required' => trans('validation.required', ['attribute' => 'email']), //'required' => trans('validation.required', ['attribute' => 'email']),
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
]; ];

View File

@ -40,11 +40,11 @@ class UpdateVendorRequest extends Request
if($this->number) if($this->number)
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id); $rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
if($this->id_number) // if($this->id_number)
$rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id); // $rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
$rules['contacts.*.email'] = 'nullable|distinct'; $rules['contacts.*.email'] = 'nullable|distinct';
// $rules['id_number'] = 'unique:vendors,id_number,'.$this->id.',id,company_id,'.auth()->user()->company()->id;
return $rules; return $rules;
} }

View File

@ -21,6 +21,8 @@ class CanStoreClientsRule implements Rule
{ {
public $company_id; public $company_id;
public $company;
public function __construct($company_id) public function __construct($company_id)
{ {
$this->company_id = $company_id; $this->company_id = $company_id;
@ -33,9 +35,9 @@ class CanStoreClientsRule implements Rule
*/ */
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
$company = Company::find($this->company_id); $this->company = Company::find($this->company_id);
return $company->clients->count() < $company->account->hosted_client_count; return $this->company->clients()->count() < $this->company->account->hosted_client_count;
} }
/** /**
@ -43,6 +45,6 @@ class CanStoreClientsRule implements Rule
*/ */
public function message() public function message()
{ {
return ctrans('texts.limit_clients', ['count' => $company->account->hosted_client_count]); return ctrans('texts.limit_clients', ['count' => $this->company->account->hosted_client_count]);
} }
} }

View File

@ -36,7 +36,7 @@ class PaymentTransformer extends BaseTransformer
} }
$transformed = [ $transformed = [
'company_id' => $this->maps['company']->id, 'company_id' => $this->company->id,
'number' => $this->getString($data, 'payment.number'), 'number' => $this->getString($data, 'payment.number'),
'user_id' => $this->getString($data, 'payment.user_id'), 'user_id' => $this->getString($data, 'payment.user_id'),
'amount' => $this->getFloat($data, 'payment.amount'), 'amount' => $this->getFloat($data, 'payment.amount'),

View File

@ -259,7 +259,7 @@ class CompanyExport implements ShouldQueue
$this->export_data['invoices'] = $this->company->invoices()->orderBy('number', 'DESC')->cursor()->map(function ($invoice){ $this->export_data['invoices'] = $this->company->invoices()->orderBy('number', 'DESC')->cursor()->map(function ($invoice){
$invoice = $this->transformBasicEntities($invoice); $invoice = $this->transformBasicEntities($invoice);
$invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']); $invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','project_id']);
return $invoice->makeVisible(['id', return $invoice->makeVisible(['id',
'private_notes', 'private_notes',

View File

@ -91,8 +91,9 @@ class ZipCredits implements ShouldQueue
foreach ($this->credits as $credit) { foreach ($this->credits as $credit) {
$download_file = file_get_contents($credit->pdf_file_path($invitation, 'url', true)); $file = $credit->service()->getCreditPdf($credit->invitations()->first());
$zipFile->addFromString(basename($credit->pdf_file_path($invitation)), $download_file); $zip_file_name = basename($file);
$zipFile->addFromString($zip_file_name, Storage::get($file));
} }

View File

@ -91,8 +91,12 @@ class ZipInvoices implements ShouldQueue
foreach ($this->invoices as $invoice) { foreach ($this->invoices as $invoice) {
$download_file = file_get_contents($invoice->pdf_file_path($invitation, 'url', true)); $file = $invoice->service()->getInvoicePdf();
$zipFile->addFromString(basename($invoice->pdf_file_path($invitation)), $download_file); $zip_file_name = basename($file);
$zipFile->addFromString($zip_file_name, Storage::get($file));
//$download_file = file_get_contents($invoice->pdf_file_path($invitation, 'url', true));
//$zipFile->addFromString(basename($invoice->pdf_file_path($invitation)), $download_file);
} }

View File

@ -0,0 +1,116 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Jobs\Ninja;
use App\DataMapper\Transactions\ClientStatusTransaction;
use App\DataMapper\Transactions\GatewayPaymentMadeTransaction;
use App\DataMapper\Transactions\InvoiceCancelledTransaction;
use App\DataMapper\Transactions\InvoiceDeletedTransaction;
use App\DataMapper\Transactions\InvoiceFeeAppliedTransaction;
use App\DataMapper\Transactions\InvoicePaymentTransaction;
use App\DataMapper\Transactions\InvoiceReversalTransaction;
use App\DataMapper\Transactions\InvoiceUpdatedTransaction;
use App\DataMapper\Transactions\MarkPaidTransaction;
use App\DataMapper\Transactions\PaymentAppliedTransaction;
use App\DataMapper\Transactions\PaymentDeletedTransaction;
use App\DataMapper\Transactions\PaymentFailedTransaction;
use App\DataMapper\Transactions\PaymentMadeTransaction;
use App\DataMapper\Transactions\PaymentRefundedTransaction;
use App\Libraries\MultiDB;
use App\Models\TransactionEvent;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class TransactionLog implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private array $payload;
private string $db;
private array $data;
private $event;
private $event_transformer;
private array $transformer_array = [
TransactionEvent::INVOICE_MARK_PAID => MarkPaidTransaction::class, //
TransactionEvent::INVOICE_UPDATED => InvoiceUpdatedTransaction::class, //
TransactionEvent::INVOICE_DELETED => InvoiceDeletedTransaction::class, //
TransactionEvent::INVOICE_PAYMENT_APPLIED => InvoicePaymentTransaction::class,
TransactionEvent::INVOICE_CANCELLED => InvoiceCancelledTransaction::class,
TransactionEvent::INVOICE_REVERSED => InvoiceReversalTransaction::class, //
TransactionEvent::INVOICE_FEE_APPLIED => InvoiceFeeAppliedTransaction::class, //
TransactionEvent::PAYMENT_MADE => PaymentMadeTransaction::class, //
TransactionEvent::GATEWAY_PAYMENT_MADE => GatewayPaymentMadeTransaction::class, //
TransactionEvent::PAYMENT_APPLIED => PaymentAppliedTransaction::class,
TransactionEvent::PAYMENT_REFUND => PaymentRefundedTransaction::class, //
TransactionEvent::PAYMENT_FAILED => PaymentFailedTransaction::class,
TransactionEvent::PAYMENT_DELETED => PaymentDeletedTransaction::class, //
TransactionEvent::CLIENT_STATUS => ClientStatusTransaction::class, //
];
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($event, $data, $db)
{
$this->db = $db;
$this->event = $event;
$this->data = $data;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// if(!Ninja::isHosted())
// return;
$this->setTransformer();
$this->payload = $this->event_transformer->transform($this->data);
$this->persist();
}
private function setTransformer()
{
$class = $this->transformer_array[$this->event];
$this->event_transformer = new $class();
return $this;
}
private function persist()
{
MultiDB::setDB($this->db);
TransactionEvent::create($this->payload);
}
}

View File

@ -92,8 +92,12 @@ class ZipQuotes implements ShouldQueue
foreach ($this->quotes as $quote) { foreach ($this->quotes as $quote) {
$download_file = file_get_contents($quote->pdf_file_path($invitation, 'url', true)); $file = $quote->service()->getQuotePdf();
$zipFile->addFromString(basename($quote->pdf_file_path($invitation)), $download_file); $zip_file_name = basename($file);
$zipFile->addFromString($zip_file_name, Storage::get($file));
// $download_file = file_get_contents($quote->pdf_file_path($invitation, 'url', true));
// $zipFile->addFromString(basename($quote->pdf_file_path($invitation)), $download_file);
} }

View File

@ -237,7 +237,8 @@ class Import implements ShouldQueue
//company size check //company size check
if ($this->company->invoices()->count() > 500 || $this->company->products()->count() > 500 || $this->company->clients()->count() > 500) { if ($this->company->invoices()->count() > 500 || $this->company->products()->count() > 500 || $this->company->clients()->count() > 500) {
$this->company->is_large = true; // $this->company->is_large = true;
$this->company->account->companies()->update(['is_large' => true]);
} }
@ -649,6 +650,7 @@ class Import implements ShouldQueue
if(array_key_exists('updated_at', $modified)) if(array_key_exists('updated_at', $modified))
$client->updated_at = Carbon::parse($modified['updated_at']); $client->updated_at = Carbon::parse($modified['updated_at']);
$client->country_id = array_key_exists('country_id', $modified) ? $modified['country_id'] : $this->company->settings->country_id;
$client->save(['timestamps' => false]); $client->save(['timestamps' => false]);
$client->fresh(); $client->fresh();

View File

@ -47,6 +47,9 @@ class InvitationViewedListener implements ShouldQueue
$entity_name = lcfirst(class_basename($event->entity)); $entity_name = lcfirst(class_basename($event->entity));
$invitation = $event->invitation; $invitation = $event->invitation;
if($entity_name == 'recurringInvoice')
return;
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new EntityViewedObject($invitation, $entity_name))->build() ); $nmo->mailable = new NinjaMailer( (new EntityViewedObject($invitation, $entity_name))->build() );
$nmo->company = $invitation->company; $nmo->company = $invitation->company;

View File

@ -153,7 +153,7 @@ class Client extends BaseModel implements HasLocalePreference
public function gateway_tokens() public function gateway_tokens()
{ {
return $this->hasMany(ClientGatewayToken::class); return $this->hasMany(ClientGatewayToken::class)->orderBy('is_default', 'ASC');
} }
public function expenses() public function expenses()
@ -840,4 +840,17 @@ class Client extends BaseModel implements HasLocalePreference
return $offset; return $offset;
} }
public function transaction_event()
{
$client = $this->fresh();
return [
'client_id' => $client->id,
'client_balance' => $client->balance ?: 0,
'client_paid_to_date' => $client->paid_to_date ?: 0,
'client_credit_balance' => $client->credit_balance ?: 0
];
}
} }

View File

@ -476,7 +476,8 @@ class Company extends BaseModel
public function owner() public function owner()
{ {
return $this->company_users->where('is_owner', true)->first()->user; return $this->company_users()->withTrashed()->where('is_owner', true)->first()->user;
//return $this->company_users->where('is_owner', true)->first()->user;
} }
public function resolveRouteBinding($value, $field = null) public function resolveRouteBinding($value, $field = null)

View File

@ -302,4 +302,16 @@ class Credit extends BaseModel
}); });
} }
public function transaction_event()
{
$credit = $this->fresh();
return [
'credit_id' => $credit->id,
'credit_amount' => $credit->amount ?: 0,
'credit_balance' => $credit->balance ?: 0,
'credit_status' => $credit->status_id ?: 1,
];
}
} }

View File

@ -103,7 +103,7 @@ class Gateway extends StaticModel
case 20: case 20:
return [ return [
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable','charge.succeeded']], GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable','charge.succeeded','payment_intent.succeeded']],
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false], GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false], GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], //Stripe GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], //Stripe

View File

@ -232,6 +232,11 @@ class Invoice extends BaseModel
{ {
return $this->hasMany(Expense::class); return $this->hasMany(Expense::class);
} }
public function expense()
{
return $this->hasOne(Expense::class);
}
/** /**
* Service entry points. * Service entry points.
*/ */
@ -535,4 +540,19 @@ class Invoice extends BaseModel
break; break;
} }
} }
public function transaction_event()
{
$invoice = $this->fresh();
return [
'invoice_id' => $invoice->id,
'invoice_amount' => $invoice->amount ?: 0,
'invoice_partial' => $invoice->partial ?: 0,
'invoice_balance' => $invoice->balance ?: 0,
'invoice_paid_to_date' => $invoice->paid_to_date ?: 0,
'invoice_status' => $invoice->status_id ?: 1,
];
}
} }

View File

@ -23,6 +23,7 @@ use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Payment\Refundable; use App\Utils\Traits\Payment\Refundable;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
class Payment extends BaseModel class Payment extends BaseModel
{ {
@ -149,6 +150,15 @@ class Payment extends BaseModel
return $this->belongsTo(PaymentType::class); return $this->belongsTo(PaymentType::class);
} }
public function translatedType()
{
if(!$this->type)
return '';
return ctrans('texts.payment_type_'.$this->type->name);
}
public function gateway_type() public function gateway_type()
{ {
return $this->belongsTo(GatewayType::class); return $this->belongsTo(GatewayType::class);
@ -314,4 +324,19 @@ class Payment extends BaseModel
} }
public function transaction_event()
{
$payment = $this->fresh();
return [
'payment_id' => $payment->id,
'payment_amount' => $payment->amount ?: 0,
'payment_applied' => $payment->applied ?: 0,
'payment_refunded' => $payment->refunded ?: 0,
'payment_status' => $payment->status_id ?: 1,
'paymentables' => $payment->paymentables->toArray(),
'payment_request' => request() ? request()->all() : [],
];
}
} }

View File

@ -0,0 +1,46 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Models;
/**
* Class Bank.
*/
class TransactionEvent extends StaticModel
{
public $timestamps = false;
public $guarded = ['id'];
public $casts = [
'metadata' => 'array',
'payment_request' => 'array',
'paymentables' => 'array',
];
public const INVOICE_MARK_PAID = 1;
public const INVOICE_UPDATED = 2;
public const INVOICE_DELETED = 3;
public const INVOICE_PAYMENT_APPLIED = 4;
public const INVOICE_CANCELLED = 5;
public const INVOICE_FEE_APPLIED = 6;
public const INVOICE_REVERSED = 7;
public const PAYMENT_MADE = 100;
public const PAYMENT_APPLIED = 101;
public const PAYMENT_REFUND = 102;
public const PAYMENT_FAILED = 103;
public const GATEWAY_PAYMENT_MADE = 104;
public const PAYMENT_DELETED = 105;
public const CLIENT_STATUS = 200;
}

View File

@ -15,6 +15,7 @@ namespace App\PaymentDrivers\Authorize;
use App\PaymentDrivers\AuthorizePaymentDriver; use App\PaymentDrivers\AuthorizePaymentDriver;
use net\authorize\api\contract\v1\CreateTransactionRequest; use net\authorize\api\contract\v1\CreateTransactionRequest;
use net\authorize\api\contract\v1\CustomerProfilePaymentType; use net\authorize\api\contract\v1\CustomerProfilePaymentType;
use net\authorize\api\contract\v1\OrderType;
use net\authorize\api\contract\v1\PaymentProfileType; use net\authorize\api\contract\v1\PaymentProfileType;
use net\authorize\api\contract\v1\TransactionRequestType; use net\authorize\api\contract\v1\TransactionRequestType;
use net\authorize\api\controller\CreateTransactionController; use net\authorize\api\controller\CreateTransactionController;
@ -42,9 +43,21 @@ class ChargePaymentProfile
$paymentProfile->setPaymentProfileId($payment_profile_id); $paymentProfile->setPaymentProfileId($payment_profile_id);
$profileToCharge->setPaymentProfile($paymentProfile); $profileToCharge->setPaymentProfile($paymentProfile);
$invoice_numbers = '';
if($this->authorize->payment_hash->data)
$invoice_numbers = collect($this->authorize->payment_hash->data->invoices)->pluck('invoice_number')->implode(',');
$description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->authorize->client->present()->name()}";
$order = new OrderType();
$order->setInvoiceNumber($invoice_numbers);
$order->setDescription($description);
$transactionRequestType = new TransactionRequestType(); $transactionRequestType = new TransactionRequestType();
$transactionRequestType->setTransactionType('authCaptureTransaction'); $transactionRequestType->setTransactionType('authCaptureTransaction');
$transactionRequestType->setAmount($amount); $transactionRequestType->setAmount($amount);
$transactionRequestType->setOrder($order);
$transactionRequestType->setProfile($profileToCharge); $transactionRequestType->setProfile($profileToCharge);
$transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code); $transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code);

View File

@ -20,6 +20,7 @@ use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject; use App\Jobs\Mail\NinjaMailerObject;
use App\Jobs\Mail\PaymentFailedMailer; use App\Jobs\Mail\PaymentFailedMailer;
use App\Jobs\Ninja\TransactionLog;
use App\Jobs\Util\SystemLogger; use App\Jobs\Util\SystemLogger;
use App\Mail\Admin\ClientPaymentFailureObject; use App\Mail\Admin\ClientPaymentFailureObject;
use App\Models\Client; use App\Models\Client;
@ -32,6 +33,7 @@ use App\Models\Payment;
use App\Models\PaymentHash; use App\Models\PaymentHash;
use App\Models\PaymentType; use App\Models\PaymentType;
use App\Models\SystemLog; use App\Models\SystemLog;
use App\Models\TransactionEvent;
use App\Services\Subscription\SubscriptionService; use App\Services\Subscription\SubscriptionService;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -317,11 +319,23 @@ class BaseDriver extends AbstractPaymentDriver
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_invoices, 'invoice_id')))->withTrashed()->get(); $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_invoices, 'invoice_id')))->withTrashed()->get();
$invoices->each(function ($invoice) use ($fee_total) { $invoices->each(function ($invoice) use ($fee_total) {
if (collect($invoice->line_items)->contains('type_id', '3')) { if (collect($invoice->line_items)->contains('type_id', '3')) {
$invoice->service()->toggleFeesPaid()->save(); $invoice->service()->toggleFeesPaid()->save();
$invoice->client->service()->updateBalance($fee_total)->save(); $invoice->client->service()->updateBalance($fee_total)->save();
$invoice->ledger()->updateInvoiceBalance($fee_total, "Gateway fee adjustment for invoice {$invoice->number}"); $invoice->ledger()->updateInvoiceBalance($fee_total, "Gateway fee adjustment for invoice {$invoice->number}");
} }
$transaction = [
'invoice' => $invoice->transaction_event(),
'payment' => [],
'client' => $invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_FEE_APPLIED, $transaction, $invoice->company->db);
}); });
} }

View File

@ -146,9 +146,21 @@ class CreditCard
} }
$invoice_numbers = '';
if($this->eway_driver->payment_hash->data)
$invoice_numbers = collect($this->eway_driver->payment_hash->data->invoices)->pluck('invoice_number')->implode(',');
$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()}";
$transaction = [ $transaction = [
'Payment' => [ 'Payment' => [
'TotalAmount' => $this->convertAmountForEway(), 'TotalAmount' => $this->convertAmountForEway(),
'CurrencyCode' => $this->eway_driver->client->currency()->code,
'InvoiceNumber' => $invoice_numbers,
'InvoiceReference' => $description,
], ],
'TransactionType' => \Eway\Rapid\Enum\TransactionType::PURCHASE, 'TransactionType' => \Eway\Rapid\Enum\TransactionType::PURCHASE,
'SecuredCardData' => $request->input('securefieldcode'), 'SecuredCardData' => $request->input('securefieldcode'),
@ -225,12 +237,22 @@ class CreditCard
{ {
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
if($this->eway_driver->payment_hash->data)
$invoice_numbers = collect($this->eway_driver->payment_hash->data->invoices)->pluck('invoice_number')->implode(',');
$description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->eway_driver->client->present()->name()}";
$transaction = [ $transaction = [
'Customer' => [ 'Customer' => [
'TokenCustomerID' => $token, 'TokenCustomerID' => $token,
], ],
'Payment' => [ 'Payment' => [
'TotalAmount' => $this->convertAmountForEway($amount), 'TotalAmount' => $this->convertAmountForEway($amount),
'CurrencyCode' => $this->eway_driver->client->currency()->code,
'InvoiceNumber' => $invoice_numbers,
'InvoiceDescription' => $description,
'InvoiceReference' => $description,
], ],
'TransactionType' => \Eway\Rapid\Enum\TransactionType::RECURRING, 'TransactionType' => \Eway\Rapid\Enum\TransactionType::RECURRING,
]; ];

View File

@ -43,6 +43,14 @@ class Token
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
$invoice_numbers = '';
if($this->eway_driver->payment_hash->data)
$invoice_numbers = collect($this->eway_driver->payment_hash->data->invoices)->pluck('invoice_number')->implode(',');
$description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->eway_driver->client->present()->name()}";
$this->eway_driver->payment_hash = $payment_hash; $this->eway_driver->payment_hash = $payment_hash;
$transaction = [ $transaction = [
@ -51,6 +59,10 @@ class Token
], ],
'Payment' => [ 'Payment' => [
'TotalAmount' => $this->eway_driver->convertAmount($amount), 'TotalAmount' => $this->eway_driver->convertAmount($amount),
'CurrencyCode' => $this->eway_driver->client->currency()->code,
'InvoiceNumber' => $invoice_numbers,
'InvoiceDescription' => $description,
'InvoiceReference' => $description,
], ],
'TransactionType' => \Eway\Rapid\Enum\TransactionType::RECURRING, 'TransactionType' => \Eway\Rapid\Enum\TransactionType::RECURRING,
]; ];

View File

@ -154,6 +154,7 @@ class ACSS
'interval_description' => 'when any invoice becomes due', 'interval_description' => 'when any invoice becomes due',
'transaction_type' => 'personal' // TODO: check if is company or personal https://stripe.com/docs/payments/acss-debit 'transaction_type' => 'personal' // TODO: check if is company or personal https://stripe.com/docs/payments/acss-debit
], ],
'verification_method' => 'instant',
] ]
] ]
], $this->stripe->stripe_connect_auth); ], $this->stripe->stripe_connect_auth);
@ -183,7 +184,7 @@ class ACSS
$this->stripe->payment_hash->save(); $this->stripe->payment_hash->save();
if (property_exists($gateway_response, 'status') && $gateway_response->status == 'processing') { if (property_exists($gateway_response, 'status') && $gateway_response->status == 'processing') {
$this->storePaymentMethod($gateway_response); // $this->storePaymentMethod($gateway_response);
return $this->processSuccessfulPayment($gateway_response->id); return $this->processSuccessfulPayment($gateway_response->id);
} }
return $this->processUnsuccessfulPayment(); return $this->processUnsuccessfulPayment();
@ -243,12 +244,13 @@ class ACSS
private function storePaymentMethod($intent) private function storePaymentMethod($intent)
{ {
try { try {
$method = $this->stripe->getStripePaymentMethod($intent->payment_method); $method = $this->stripe->getStripePaymentMethod($intent->payment_method);
$payment_meta = new \stdClass; $payment_meta = new \stdClass;
$payment_meta->brand = (string) \sprintf('%s (%s)', $method->au_becs_debit->bank_code, ctrans('texts.acss')); $payment_meta->brand = (string) $method->acss_debit->bank_name;
$payment_meta->last4 = (string) $method->au_becs_debit->last4; $payment_meta->last4 = (string) $method->acss_debit->last4;
$payment_meta->state = 'authorized'; $payment_meta->state = 'authorized';
$payment_meta->type = GatewayType::ACSS; $payment_meta->type = GatewayType::ACSS;

View File

@ -198,14 +198,6 @@ class BrowserPay implements MethodInterface
return; return;
} }
// $domain = config('ninja.app_url');
// if (Ninja::isHosted()) {
// $domain = isset($this->stripe->company_gateway->company->portal_domain)
// ? $this->stripe->company_gateway->company->portal_domain
// : $this->stripe->company_gateway->company->domain();
// }
$domain = $this->getAppleDomain(); $domain = $this->getAppleDomain();
if(!$domain) if(!$domain)
@ -244,12 +236,7 @@ class BrowserPay implements MethodInterface
$domain = config('ninja.app_url'); $domain = config('ninja.app_url');
} }
$parsed_url = parse_url($domain); return str_replace("https://", "", $domain);
if(array_key_exists('host', $parsed_url))
return $parsed_url['host'];
return false;
} }

View File

@ -76,11 +76,9 @@ class ImportCustomers
$account = $this->stripe->company_gateway->company->account; $account = $this->stripe->company_gateway->company->account;
if(!$account->isPaidHostedClient() && Client::where('company_id', $this->stripe->company_gateway->company_id)->count() > config('ninja.quotas.free.clients')) if(Ninja::isHosted() && !$account->isPaidHostedClient() && Client::where('company_id', $this->stripe->company_gateway->company_id)->count() > config('ninja.quotas.free.clients'))
return; return;
// nlog("search Stripe for {$customer->id}");
$existing_customer_token = $this->stripe $existing_customer_token = $this->stripe
->company_gateway ->company_gateway
->client_gateway_tokens() ->client_gateway_tokens()
@ -104,9 +102,6 @@ class ImportCustomers
return; return;
} }
// nlog("inserting a customer");
//nlog($customer);
$client = ClientFactory::create($this->stripe->company_gateway->company_id, $this->stripe->company_gateway->user_id); $client = ClientFactory::create($this->stripe->company_gateway->company_id, $this->stripe->company_gateway->user_id);
if($customer->address) if($customer->address)

View File

@ -33,33 +33,10 @@ class UpdatePaymentMethods
$this->stripe = $stripe; $this->stripe = $stripe;
} }
// public function run()
// {
// $this->stripe->init();
// $this->stripe
// ->company_gateway
// ->client_gateway_tokens
// ->each(function ($token){
// // $bank_accounts = Customer::allSources(
// // $token->gateway_customer_reference,
// // ['object' => 'bank_account', 'limit' => 300]
// // );
// // foreach($bank_accounts as $bank_account)
// // {
// // $this->addOrUpdateBankAccount($bank_account, $token);
// // }
// // $this->processCustomer($token->gateway_customer_reference);
// });
// }
public function updateMethods(Customer $customer, Client $client) public function updateMethods(Customer $customer, Client $client)
{ {
$this->stripe->client = $client;
$card_methods = PaymentMethod::all([ $card_methods = PaymentMethod::all([
'customer' => $customer->id, 'customer' => $customer->id,
'type' => 'card', 'type' => 'card',
@ -93,37 +70,52 @@ class UpdatePaymentMethods
$this->addOrUpdateCard($method, $customer->id, $client, GatewayType::SOFORT); $this->addOrUpdateCard($method, $customer->id, $client, GatewayType::SOFORT);
} }
//$this->importBankAccounts($customer, $client); $this->importBankAccounts($customer, $client);
} }
private function importBankAccounts($customer, $client) private function importBankAccounts($customer, $client)
{ {
$sources = $customer->sources;
foreach($sources->data as $method)
{
$token_exists = ClientGatewayToken::where([
'gateway_customer_reference' => $customer->id,
'token' => $method->id,
'client_id' => $client->id,
'company_id' => $client->company_id,
])->exists();
/* Already exists return */
if($token_exists)
continue;
$payment_meta = new \stdClass;
$payment_meta->brand = (string) \sprintf('%s (%s)', $method->bank_name, ctrans('texts.ach'));
$payment_meta->last4 = (string) $method->last4;
$payment_meta->type = GatewayType::BANK_TRANSFER;
$payment_meta->state = $method->status;
$data = [
'payment_meta' => $payment_meta,
'token' => $method->id,
'payment_method_id' => GatewayType::BANK_TRANSFER,
];
$additional_data = ['gateway_customer_reference' => $customer->id];
if($customer->default_source === $method->id)
$additional_data = ['gateway_customer_reference' => $customer->id, 'is_default', 1];
$this->stripe->storeGatewayToken($data, $additional_data);
}
} }
// private function addOrUpdateBankAccount($bank_account, $customer_reference, Client $client)
// {
// $token_exists = ClientGatewayToken::where([
// 'gateway_customer_reference' => $customer_reference,
// 'token' => $bank_account->id,
// ])->exists();
// /* Already exists return */
// if($token_exists)
// return;
// $cgt = ClientGatewayTokenFactory::create($client->company_id);
// $cgt->client_id = $client->id;
// $cgt->token = $bank_account->id;
// $cgt->gateway_customer_reference = $customer_reference;
// $cgt->company_gateway_id = $this->stripe->company_gateway->id;
// $cgt->gateway_type_id = GatewayType::BANK_TRANSFER;
// $cgt->meta = new \stdClass;
// $cgt->routing_number = $bank_account->routing_number;
// $cgt->save();
// }
private function addOrUpdateCard(PaymentMethod $method, $customer_reference, Client $client, $type_id) private function addOrUpdateCard(PaymentMethod $method, $customer_reference, Client $client, $type_id)
{ {

View File

@ -533,12 +533,10 @@ class StripePaymentDriver extends BaseDriver
//payment_intent.succeeded - this will confirm or cancel the payment //payment_intent.succeeded - this will confirm or cancel the payment
if($request->type === 'payment_intent.succeeded'){ if($request->type === 'payment_intent.succeeded'){
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(10); PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(10);
// PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id);
return response()->json([], 200); return response()->json([], 200);
} }
if ($request->type === 'charge.succeeded') { if ($request->type === 'charge.succeeded') {
// if ($request->type === 'charge.succeeded' || $request->type === 'payment_intent.succeeded') {
foreach ($request->data as $transaction) { foreach ($request->data as $transaction) {

View File

@ -222,6 +222,9 @@ class BaseRepository
if (array_key_exists('documents', $data)) if (array_key_exists('documents', $data))
$this->saveDocuments($data['documents'], $model); $this->saveDocuments($data['documents'], $model);
if (array_key_exists('file', $data))
$this->saveDocuments($data['file'], $model);
/* If invitations are present we need to filter existing invitations with the new ones */ /* If invitations are present we need to filter existing invitations with the new ones */
if (isset($data['invitations'])) { if (isset($data['invitations'])) {
$invitations = collect($data['invitations']); $invitations = collect($data['invitations']);

View File

@ -14,11 +14,13 @@ namespace App\Repositories;
use App\Events\Payment\PaymentWasCreated; use App\Events\Payment\PaymentWasCreated;
use App\Events\Payment\PaymentWasDeleted; use App\Events\Payment\PaymentWasDeleted;
use App\Jobs\Credit\ApplyCreditPayment; use App\Jobs\Credit\ApplyCreditPayment;
use App\Jobs\Ninja\TransactionLog;
use App\Libraries\Currency\Conversion\CurrencyApi; use App\Libraries\Currency\Conversion\CurrencyApi;
use App\Models\Client; use App\Models\Client;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\TransactionEvent;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments; use App\Utils\Traits\SavesDocuments;
@ -94,11 +96,8 @@ class PaymentRepository extends BaseRepository {
if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) { if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) {
$_credit_totals = array_sum(array_column($data['credits'], 'amount')); $_credit_totals = array_sum(array_column($data['credits'], 'amount'));
// if ($data['amount'] == $_credit_totals) { $client->service()->updatePaidToDate($_credit_totals)->save();
// $data['amount'] = 0;
// } else {
$client->service()->updatePaidToDate($_credit_totals)->save();
// }
} }
} }
@ -181,10 +180,19 @@ class PaymentRepository extends BaseRepository {
} }
$payment->applied += ($invoice_totals - $credit_totals); //wont work because - check tests $payment->applied += ($invoice_totals - $credit_totals); //wont work because - check tests
// $payment->applied += $invoice_totals; //wont work because - check tests
$payment->save(); $payment->save();
$transaction = [
'invoice' => [],
'payment' => $payment->transaction_event(),
'client' => $payment->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::PAYMENT_MADE, $transaction, $payment->company->db);
return $payment->fresh(); return $payment->fresh();
} }

View File

@ -182,4 +182,50 @@ class TaskRepository extends BaseRepository
}); });
} }
public function start(Task $task)
{
if(strlen($task->time_log) < 5)
{
$log = [];
$log = array_merge($log, [[time(),0]]);
$task->time_log = json_encode($log);
$task->save();
}
$log = json_decode($task->time_log,true);;
$last = end($log);
if($last[1] !== 0){
$new = [time(), 0];
$log = array_merge($log, [$new]);
$task->time_log = json_encode($log);
$task->save();
}
}
public function stop(Task $task)
{
$log = json_decode($task->time_log,true);;
$last = end($log);
if($last[1] === 0){
$last[1] = time();
array_pop($log);
$log = array_merge($log, [$last]);
$task->time_log = json_encode($log);
$task->save();
}
}
} }

View File

@ -28,21 +28,27 @@ class ClientService
public function updateBalance(float $amount) public function updateBalance(float $amount)
{ {
$this->client->balance += $amount; // $this->client->balance += $amount;
$this->client->increment('balance', $amount);
return $this; return $this;
} }
public function updatePaidToDate(float $amount) public function updatePaidToDate(float $amount)
{ {
$this->client->paid_to_date += $amount; // $this->client->paid_to_date += $amount;
$this->client->increment('paid_to_date', $amount);
return $this; return $this;
} }
public function adjustCreditBalance(float $amount) public function adjustCreditBalance(float $amount)
{ {
$this->client->credit_balance += $amount; // $this->client->credit_balance += $amount;
$this->client->increment('credit_balance', $amount);
return $this; return $this;
} }

View File

@ -133,11 +133,16 @@ class CreditService
->attach($this->credit->id, ['amount' => $adjustment]); ->attach($this->credit->id, ['amount' => $adjustment]);
//reduce client paid_to_date by $this->credit->balance amount //reduce client paid_to_date by $this->credit->balance amount
$this->credit // $this->credit
->client // ->client
->service() // ->service()
->updatePaidToDate($adjustment) // ->updatePaidToDate($adjustment)
->save(); // ->save();
$client = $this->credit->client->fresh();
$client->service()
->updatePaidToDate($adjustment)
->save();
event('eloquent.created: App\Models\Payment', $payment); event('eloquent.created: App\Models\Payment', $payment);
@ -162,21 +167,24 @@ class CreditService
public function adjustBalance($adjustment) public function adjustBalance($adjustment)
{ {
$this->credit->balance += $adjustment; // $this->credit->balance += $adjustment;
$this->credit->increment('balance', $adjustment);
return $this; return $this;
} }
public function updatePaidToDate($adjustment) public function updatePaidToDate($adjustment)
{ {
$this->credit->paid_to_date += $adjustment; // $this->credit->paid_to_date += $adjustment;
$this->credit->increment('paid_to_date', $adjustment);
return $this; return $this;
} }
public function updateBalance($adjustment) public function updateBalance($adjustment)
{ {
$this->credit->balance -= $adjustment; // $this->credit->balance -= $adjustment;
$this->credit->decrement('balance', $adjustment);
return $this; return $this;
} }

View File

@ -34,7 +34,7 @@ class GetCreditPdf extends AbstractService
public function run() public function run()
{ {
if (! $this->contact) { if (! $this->contact) {
$this->contact = $this->credit->client->primary_contact()->first(); $this->contact = $this->credit->client->primary_contact()->first() ?: $this->credit->client->contacts()->first();
} }
$path = $this->credit->client->credit_filepath($this->invitation); $path = $this->credit->client->credit_filepath($this->invitation);

View File

@ -11,8 +11,10 @@
namespace App\Services\Invoice; namespace App\Services\Invoice;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\TransactionEvent;
use App\Services\AbstractService; use App\Services\AbstractService;
class ApplyPayment extends AbstractService class ApplyPayment extends AbstractService
@ -128,6 +130,16 @@ class ApplyPayment extends AbstractService
$this->invoice->service()->applyNumber()->workFlow()->save(); $this->invoice->service()->applyNumber()->workFlow()->save();
$transaction = [
'invoice' => $this->invoice->transaction_event(),
'payment' => $this->payment->transaction_event(),
'client' => $this->invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_PAYMENT_APPLIED, $transaction, $this->invoice->company->db);
return $this->invoice; return $this->invoice;
} }
} }

View File

@ -189,8 +189,8 @@ class AutoBillInvoice extends AbstractService
->adjustCreditBalance($amount * -1) ->adjustCreditBalance($amount * -1)
->save(); ->save();
$this->invoice->ledger() $this->invoice->ledger() //09-03-2022
->updateInvoiceBalance($amount * -1, "Invoice {$this->invoice->number} payment using Credit {$current_credit->number}") // ->updateInvoiceBalance($amount * -1, "Invoice {$this->invoice->number} payment using Credit {$current_credit->number}")
->updateCreditBalance($amount * -1, "Credit {$current_credit->number} used to pay down Invoice {$this->invoice->number}") ->updateCreditBalance($amount * -1, "Credit {$current_credit->number} used to pay down Invoice {$this->invoice->number}")
->save(); ->save();

View File

@ -30,7 +30,7 @@ class GetInvoicePdf extends AbstractService
public function run() public function run()
{ {
if (! $this->contact) { if (! $this->contact) {
$this->contact = $this->invoice->client->primary_contact()->first(); $this->contact = $this->invoice->client->primary_contact()->first() ?: $this->invoice->client->contacts()->first();
} }
$invitation = $this->invoice->invitations->where('client_contact_id', $this->contact->id)->first(); $invitation = $this->invoice->invitations->where('client_contact_id', $this->contact->id)->first();

View File

@ -12,8 +12,10 @@
namespace App\Services\Invoice; namespace App\Services\Invoice;
use App\Events\Invoice\InvoiceWasCancelled; use App\Events\Invoice\InvoiceWasCancelled;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Client; use App\Models\Client;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\TransactionEvent;
use App\Services\AbstractService; use App\Services\AbstractService;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
@ -52,6 +54,16 @@ class HandleCancellation extends AbstractService
event(new InvoiceWasCancelled($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new InvoiceWasCancelled($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$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; return $this->invoice;
} }

View File

@ -15,11 +15,13 @@ use App\Events\Invoice\InvoiceWasReversed;
use App\Factory\CreditFactory; use App\Factory\CreditFactory;
use App\Factory\InvoiceItemFactory; use App\Factory\InvoiceItemFactory;
use App\Helpers\Invoice\InvoiceSum; use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Client; use App\Models\Client;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\Paymentable; use App\Models\Paymentable;
use App\Models\TransactionEvent;
use App\Services\AbstractService; use App\Services\AbstractService;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
@ -136,6 +138,16 @@ class HandleReversal extends AbstractService
event(new InvoiceWasReversed($this->invoice, $this->invoice->company, Ninja::eventVars())); event(new InvoiceWasReversed($this->invoice, $this->invoice->company, Ninja::eventVars()));
$transaction = [
'invoice' => $this->invoice->transaction_event(),
'payment' => [],
'client' => $this->invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_REVERSED, $transaction, $this->invoice->company->db);
return $this->invoice; return $this->invoice;
//create a ledger row for this with the resulting Credit ( also include an explanation in the notes section ) //create a ledger row for this with the resulting Credit ( also include an explanation in the notes section )
} }

View File

@ -145,7 +145,10 @@ class InvoiceService
return $this; return $this;
} }
$this->invoice->balance += $balance_adjustment; // $this->invoice->balance += $balance_adjustment;
$this->invoice->increment('balance', $balance_adjustment);
if (round($this->invoice->balance,2) == 0 && !$is_draft) { if (round($this->invoice->balance,2) == 0 && !$is_draft) {
$this->invoice->status_id = Invoice::STATUS_PAID; $this->invoice->status_id = Invoice::STATUS_PAID;
@ -160,7 +163,10 @@ class InvoiceService
public function updatePaidToDate($adjustment) public function updatePaidToDate($adjustment)
{ {
$this->invoice->paid_to_date += $adjustment; // $this->invoice->paid_to_date += $adjustment;
$this->invoice->increment('paid_to_date', $adjustment);
return $this; return $this;
} }
@ -381,7 +387,8 @@ class InvoiceService
/*Update the partial amount of a invoice*/ /*Update the partial amount of a invoice*/
public function updatePartial($amount) public function updatePartial($amount)
{ {
$this->invoice->partial += $amount; // $this->invoice->partial += $amount;
$this->invoice->increment('partial', $amount);
return $this; return $this;
} }

View File

@ -11,7 +11,9 @@
namespace App\Services\Invoice; namespace App\Services\Invoice;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\TransactionEvent;
use App\Services\AbstractService; use App\Services\AbstractService;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -47,6 +49,16 @@ class MarkInvoiceDeleted extends AbstractService
->adjustBalance() ->adjustBalance()
->adjustLedger(); ->adjustLedger();
$transaction = [
'invoice' => $this->invoice->transaction_event(),
'payment' => $this->invoice->payments()->exists() ? $this->invoice->payments()->first()->transaction_event() : [],
'client' => $this->invoice->client->transaction_event(),
'credit' => [],
'metadata' => ['total_payments' => $this->total_payments, 'balance_adjustment' => $this->balance_adjustment, 'adjustment_amount' => $this->adjustment_amount],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_DELETED, $transaction, $this->invoice->company->db);
return $this->invoice; return $this->invoice;
} }
@ -128,11 +140,6 @@ class MarkInvoiceDeleted extends AbstractService
$this->balance_adjustment = $this->invoice->balance; $this->balance_adjustment = $this->invoice->balance;
//$this->total_payments = $this->invoice->payments->sum('amount - refunded');
// nlog("adjustment amount = {$this->adjustment_amount}");
// nlog("total payments = {$this->total_payments}");
return $this; return $this;
} }

View File

@ -15,10 +15,12 @@ use App\Events\Invoice\InvoiceWasPaid;
use App\Events\Payment\PaymentWasCreated; use App\Events\Payment\PaymentWasCreated;
use App\Factory\PaymentFactory; use App\Factory\PaymentFactory;
use App\Jobs\Invoice\InvoiceWorkflowSettings; use App\Jobs\Invoice\InvoiceWorkflowSettings;
use App\Jobs\Ninja\TransactionLog;
use App\Jobs\Payment\EmailPayment; use App\Jobs\Payment\EmailPayment;
use App\Libraries\Currency\Conversion\CurrencyApi; use App\Libraries\Currency\Conversion\CurrencyApi;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\TransactionEvent;
use App\Services\AbstractService; use App\Services\AbstractService;
use App\Services\Client\ClientService; use App\Services\Client\ClientService;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -110,6 +112,16 @@ class MarkPaid extends AbstractService
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
event(new InvoiceWasPaid($this->invoice, $payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new InvoiceWasPaid($this->invoice, $payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$transaction = [
'invoice' => $this->invoice->transaction_event(),
'payment' => $payment->transaction_event(),
'client' => $this->invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_MARK_PAID, $transaction, $this->invoice->company->db);
return $this->invoice; return $this->invoice;
} }

View File

@ -34,17 +34,20 @@ class UpdateBalance extends AbstractService
if ($this->invoice->is_deleted) { if ($this->invoice->is_deleted) {
return $this->invoice; return $this->invoice;
} }
nlog("invoice id = {$this->invoice->id}");
nlog("invoice balance = {$this->invoice->balance}");
nlog("invoice adjustment = {$this->balance_adjustment}");
$this->invoice->balance += floatval($this->balance_adjustment); nlog("invoice id = {$this->invoice->id}");
nlog("invoice balance = {$this->invoice->balance}");
nlog("invoice adjustment = {$this->balance_adjustment}");
// $this->invoice->balance += floatval($this->balance_adjustment);
$this->invoice->increment('balance', floatval($this->balance_adjustment));
if ($this->invoice->balance == 0 && !$this->is_draft) { if ($this->invoice->balance == 0 && !$this->is_draft) {
$this->invoice->status_id = Invoice::STATUS_PAID; $this->invoice->status_id = Invoice::STATUS_PAID;
} }
nlog("final balance = {$this->invoice->balance}"); nlog("final balance = {$this->invoice->balance}");
return $this->invoice; return $this->invoice;
} }

View File

@ -11,9 +11,11 @@
namespace App\Services\Payment; namespace App\Services\Payment;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\TransactionEvent;
use App\Repositories\ActivityRepository; use App\Repositories\ActivityRepository;
class DeletePayment class DeletePayment
@ -82,6 +84,8 @@ class DeletePayment
$net_deletable = $paymentable_invoice->pivot->amount - $paymentable_invoice->pivot->refunded; $net_deletable = $paymentable_invoice->pivot->amount - $paymentable_invoice->pivot->refunded;
$client = $this->payment->client->fresh();
nlog("net deletable amount - refunded = {$net_deletable}"); nlog("net deletable amount - refunded = {$net_deletable}");
if(!$paymentable_invoice->is_deleted) if(!$paymentable_invoice->is_deleted)
@ -95,17 +99,16 @@ class DeletePayment
->updateInvoiceBalance($net_deletable, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}") ->updateInvoiceBalance($net_deletable, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")
->save(); ->save();
$paymentable_invoice->client $client = $client->service()
->service() ->updateBalance($net_deletable)
->updateBalance($net_deletable) ->save();
// ->updatePaidToDate($net_deletable * -1)
->save();
if ($paymentable_invoice->balance == $paymentable_invoice->amount) { if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save(); $paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
} else { } else {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save(); $paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save();
} }
} }
else { else {
@ -118,19 +121,36 @@ class DeletePayment
} }
$transaction = [
'invoice' => $paymentable_invoice->transaction_event(),
'payment' => $this->payment->transaction_event(),
'client' => $client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::PAYMENT_DELETED, $transaction, $paymentable_invoice->company->db);
}); });
} }
// else {
/* If there are no invoices - then we need to still adjust the total client->paid_to_date amount*/ $this->payment
->client
->service()
->updatePaidToDate(($this->payment->amount - $this->payment->refunded)*-1)
->save();
$transaction = [
'invoice' => [],
'payment' => [],
'client' => $this->payment->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::CLIENT_STATUS, $transaction, $this->payment->company->db);
$this->payment
->client
->service()
->updatePaidToDate(($this->payment->amount - $this->payment->refunded)*-1)
->save();
// }
return $this; return $this;
} }

View File

@ -14,10 +14,12 @@ namespace App\Services\Payment;
use App\Exceptions\PaymentRefundFailed; use App\Exceptions\PaymentRefundFailed;
use App\Factory\CreditFactory; use App\Factory\CreditFactory;
use App\Factory\InvoiceItemFactory; use App\Factory\InvoiceItemFactory;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Activity; use App\Models\Activity;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\TransactionEvent;
use App\Repositories\ActivityRepository; use App\Repositories\ActivityRepository;
use App\Utils\Ninja; use App\Utils\Ninja;
use stdClass; use stdClass;
@ -51,7 +53,7 @@ class RefundPayment
public function run() public function run()
{ {
return $this->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment) $this->payment = $this->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment)
->setStatus() //sets status of payment ->setStatus() //sets status of payment
//->reversePayment() //->reversePayment()
//->buildCreditNote() //generate the credit note //->buildCreditNote() //generate the credit note
@ -61,6 +63,18 @@ class RefundPayment
->adjustInvoices() ->adjustInvoices()
->processGatewayRefund() //process the gateway refund if needed ->processGatewayRefund() //process the gateway refund if needed
->save(); ->save();
$transaction = [
'invoice' => [],
'payment' => $this->payment->transaction_event(),
'client' => $this->payment->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::PAYMENT_REFUND, $transaction, $this->payment->company->db);
return $this->payment;
} }
/** /**
@ -256,27 +270,51 @@ class RefundPayment
$adjustment_amount += $refunded_invoice['amount']; $adjustment_amount += $refunded_invoice['amount'];
$client->balance += $refunded_invoice['amount']; $client->balance += $refunded_invoice['amount'];
//$client->paid_to_date -= $refunded_invoice['amount'];//todo refund balancing
$client->save(); $client->save();
//todo adjust ledger balance here? or after and reference the credit and its total
$transaction = [
'invoice' => $invoice->transaction_event(),
'payment' => [],
'client' => $client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::PAYMENT_REFUND, $transaction, $invoice->company->db);
} }
// $ledger_string = "Refund for Invoice {$invoice->number} for amount " . $refunded_invoice['amount']; //todo
// $this->credit_note->ledger()->updateCreditBalance($adjustment_amount, $ledger_string);
$client = $this->payment->client->fresh(); $client = $this->payment->client->fresh();
$client->service()->updatePaidToDate(-1 * $refunded_invoice['amount'])->save(); $client->service()->updatePaidToDate(-1 * $refunded_invoice['amount'])->save();
$transaction = [
'invoice' => [],
'payment' => [],
'client' => $client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::PAYMENT_REFUND, $transaction, $client->company->db);
} }
else{ else{
//if we are refunding and no payments have been tagged, then we need to decrement the client->paid_to_date by the total refund amount. //if we are refunding and no payments have been tagged, then we need to decrement the client->paid_to_date by the total refund amount.
$client = $this->payment->client->fresh(); $client = $this->payment->client->fresh();
$client->service()->updatePaidToDate(-1 * $this->total_refund)->save(); $client->service()->updatePaidToDate(-1 * $this->total_refund)->save();
$transaction = [
'invoice' => [],
'payment' => [],
'client' => $client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::PAYMENT_REFUND, $transaction, $client->company->db);
} }
return $this; return $this;

View File

@ -13,9 +13,11 @@ namespace App\Services\Payment;
use App\Events\Invoice\InvoiceWasUpdated; use App\Events\Invoice\InvoiceWasUpdated;
use App\Jobs\Invoice\InvoiceWorkflowSettings; use App\Jobs\Invoice\InvoiceWorkflowSettings;
use App\Jobs\Ninja\TransactionLog;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Models\PaymentHash; use App\Models\PaymentHash;
use App\Models\TransactionEvent;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -88,6 +90,17 @@ class UpdateInvoicePayment
$this->payment->applied += $paid_amount; $this->payment->applied += $paid_amount;
$transaction = [
'invoice' => $invoice->transaction_event(),
'payment' => $this->payment->transaction_event(),
'client' => $client->transaction_event(),
'credit' => [],
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::GATEWAY_PAYMENT_MADE, $transaction, $invoice->company->db);
}); });
/* Remove the event updater from within the loop to prevent race conditions */ /* Remove the event updater from within the loop to prevent race conditions */

View File

@ -30,7 +30,7 @@ class GetQuotePdf extends AbstractService
public function run() public function run()
{ {
if (! $this->contact) { if (! $this->contact) {
$this->contact = $this->quote->client->primary_contact()->first(); $this->contact = $this->quote->client->primary_contact()->first() ?: $this->quote->client->contacts()->first();
} }
$invitation = $this->quote->invitations->where('client_contact_id', $this->contact->id)->first(); $invitation = $this->quote->invitations->where('client_contact_id', $this->contact->id)->first();

View File

@ -174,6 +174,16 @@ class HtmlEngine
$data['$approveButton'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_quote').'</a>', 'label' => ctrans('texts.approve')]; $data['$approveButton'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_quote').'</a>', 'label' => ctrans('texts.approve')];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_quote')]; $data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_quote')];
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.quote_date')]; $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.quote_date')];
if($this->entity->project) {
$data['$project.name'] = ['value' => $this->entity->project->name, 'label' => ctrans('texts.project_name')];
$data['$invoice.project'] = &$data['$project.name'];
}
if($this->entity->vendor) {
$data['$invoice.vendor'] = ['value' => $this->entity->vendor->present()->name(), 'label' => ctrans('texts.vendor_name')];
}
} }
if ($this->entity_string == 'credit') { if ($this->entity_string == 'credit') {
@ -317,6 +327,7 @@ class HtmlEngine
$data['$website'] = ['value' => $this->client->present()->website() ?: '&nbsp;', 'label' => ctrans('texts.website')]; $data['$website'] = ['value' => $this->client->present()->website() ?: '&nbsp;', 'label' => ctrans('texts.website')];
$data['$phone'] = ['value' => $this->client->present()->phone() ?: '&nbsp;', 'label' => ctrans('texts.phone')]; $data['$phone'] = ['value' => $this->client->present()->phone() ?: '&nbsp;', 'label' => ctrans('texts.phone')];
$data['$country'] = ['value' => isset($this->client->country->name) ? ctrans('texts.country_' . $this->client->country->name) : '', 'label' => ctrans('texts.country')]; $data['$country'] = ['value' => isset($this->client->country->name) ? ctrans('texts.country_' . $this->client->country->name) : '', 'label' => ctrans('texts.country')];
$data['$country_2'] = ['value' => isset($this->client->country) ? $this->client->country->iso_3166_2 : '', 'label' => ctrans('texts.country')];
$data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')]; $data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
$data['$client_name'] = ['value' => $this->entity->present()->clientName() ?: '&nbsp;', 'label' => ctrans('texts.client_name')]; $data['$client_name'] = ['value' => $this->entity->present()->clientName() ?: '&nbsp;', 'label' => ctrans('texts.client_name')];
$data['$client.name'] = &$data['$client_name']; $data['$client.name'] = &$data['$client_name'];
@ -392,6 +403,7 @@ class HtmlEngine
$data['$company.state'] = ['value' => $this->settings->state ?: '&nbsp;', 'label' => ctrans('texts.state')]; $data['$company.state'] = ['value' => $this->settings->state ?: '&nbsp;', 'label' => ctrans('texts.state')];
$data['$company.postal_code'] = ['value' => $this->settings->postal_code ?: '&nbsp;', 'label' => ctrans('texts.postal_code')]; $data['$company.postal_code'] = ['value' => $this->settings->postal_code ?: '&nbsp;', 'label' => ctrans('texts.postal_code')];
$data['$company.country'] = ['value' => $this->getCountryName(), 'label' => ctrans('texts.country')]; $data['$company.country'] = ['value' => $this->getCountryName(), 'label' => ctrans('texts.country')];
$data['$company.country_2'] = ['value' => $this->getCountryCode(), 'label' => ctrans('texts.country')];
$data['$company.phone'] = ['value' => $this->settings->phone ?: '&nbsp;', 'label' => ctrans('texts.phone')]; $data['$company.phone'] = ['value' => $this->settings->phone ?: '&nbsp;', 'label' => ctrans('texts.phone')];
$data['$company.email'] = ['value' => $this->settings->email ?: '&nbsp;', 'label' => ctrans('texts.email')]; $data['$company.email'] = ['value' => $this->settings->email ?: '&nbsp;', 'label' => ctrans('texts.email')];
$data['$company.vat_number'] = ['value' => $this->settings->vat_number ?: '&nbsp;', 'label' => ctrans('texts.vat_number')]; $data['$company.vat_number'] = ['value' => $this->settings->vat_number ?: '&nbsp;', 'label' => ctrans('texts.vat_number')];
@ -617,6 +629,17 @@ class HtmlEngine
return '&nbsp;'; return '&nbsp;';
} }
private function getCountryCode() :string
{
$country = Country::find($this->settings->country_id);
if ($country) {
return ctrans('texts.country_' . $country->iso_3166_2);
}
return '&nbsp;';
}
/** /**
* Due to the way we are compiling the blade template we * Due to the way we are compiling the blade template we
* have no ability to iterate, so in the case * have no ability to iterate, so in the case

View File

@ -45,6 +45,7 @@ trait SavesDocuments
$is_public $is_public
); );
} }
} }
public function saveDocument($document, $entity, $is_public = true) public function saveDocument($document, $entity, $is_public = true)

View File

@ -31,7 +31,7 @@ class TranslationHelper
return Cache::get('countries')->each(function ($country) { return Cache::get('countries')->each(function ($country) {
$country->name = ctrans('texts.country_'.$country->name); $country->name = ctrans('texts.country_'.$country->name);
})->sortBy(function ($country) { })->sortBy(function ($country) {
return $country->name; return $country->iso_3166_2;
}); });
} }

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.66', 'app_version' => '5.3.67',
'app_tag' => '5.3.66', 'app_tag' => '5.3.67',
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''), 'api_secret' => env('API_SECRET', ''),

View File

@ -4,7 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
class Onboarding extends Migration //class Onboarding extends Migration
return new class extends Migration
{ {
/** /**
* Run the migrations. * Run the migrations.
@ -34,4 +35,4 @@ class Onboarding extends Migration
} }
} }
} };

View File

@ -0,0 +1,58 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class TransactionEvents extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('transaction_events', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('client_id')->index();
$table->unsignedInteger('invoice_id');
$table->unsignedInteger('payment_id');
$table->unsignedInteger('credit_id');
$table->decimal('client_balance', 16, 4)->default(0);
$table->decimal('client_paid_to_date', 16, 4)->default(0);
$table->decimal('client_credit_balance', 16, 4)->default(0);
$table->decimal('invoice_balance', 16, 4)->default(0);
$table->decimal('invoice_amount', 16, 4)->default(0);
$table->decimal('invoice_partial', 16, 4)->default(0);
$table->decimal('invoice_paid_to_date', 16, 4)->default(0);
$table->unsignedInteger('invoice_status')->nullable();
$table->decimal('payment_amount', 16, 4)->default(0);
$table->decimal('payment_applied', 16, 4)->default(0);
$table->decimal('payment_refunded', 16, 4)->default(0);
$table->unsignedInteger('payment_status')->nullable();
$table->mediumText('paymentables')->nullable();
$table->unsignedInteger('event_id');
$table->unsignedInteger('timestamp');
$table->mediumText('payment_request')->nullable();
$table->mediumText('metadata')->nullable();
$table->decimal('credit_balance', 16, 4)->default(0);
$table->decimal('credit_amount', 16, 4)->default(0);
$table->unsignedInteger('credit_status')->nullable();
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

View File

@ -3,42 +3,42 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache'; const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache'; const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = { const RESOURCES = {
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541", "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba",
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487",
"assets/fonts/MaterialIcons-Regular.otf": "7e7a6cccddf6d7b20012a548461d5d81",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a", "assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3", "assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71", "assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978", "assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024", "assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c", "assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219", "assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3", "assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5", "assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
"assets/NOTICES": "9a4bf0423a5e265f38c4df37f7a0a913", "assets/NOTICES": "9a4bf0423a5e265f38c4df37f7a0a913",
"assets/fonts/MaterialIcons-Regular.otf": "7e7a6cccddf6d7b20012a548461d5d81", "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"/": "7c3ca4e7f4b430421585744bdd926f67",
"version.json": "443986d36b3df952ad780139ecccd516",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"main.dart.js": "c0d886e3bce647afe312a4cc2853c08f",
"favicon.png": "dca91c54388f52eded692718d5a98b8b", "favicon.png": "dca91c54388f52eded692718d5a98b8b",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", "/": "9c1820b56e212ef046fb5b877da280e6",
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900", "version.json": "443986d36b3df952ad780139ecccd516",
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd", "main.dart.js": "e82126a54ecc81baa8cf3c81f212ca69",
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487", "favicon.ico": "51636d3a390451561744c42188ccd628"
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba"
}; };
// The application shell files that are downloaded before a service worker can // The application shell files that are downloaded before a service worker can

File diff suppressed because one or more lines are too long

221559
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

224334
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More