mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 02:04:30 -04:00
commit
bf07742caa
@ -1 +1 @@
|
||||
5.5.42
|
||||
5.5.43
|
@ -41,6 +41,7 @@ use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
@ -53,7 +54,7 @@ use Illuminate\Support\Str;
|
||||
|
||||
class DemoMode extends Command
|
||||
{
|
||||
use MakesHash, GeneratesCounter;
|
||||
use MakesHash, GeneratesCounter, AppSetup;
|
||||
|
||||
protected $signature = 'ninja:demo-mode';
|
||||
|
||||
@ -83,34 +84,14 @@ class DemoMode extends Command
|
||||
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
foreach ($cached_tables as $name => $class) {
|
||||
if (! Cache::has($name)) {
|
||||
// check that the table exists in case the migration is pending
|
||||
if (! Schema::hasTable((new $class())->getTable())) {
|
||||
continue;
|
||||
}
|
||||
if ($name == 'payment_terms') {
|
||||
$orderBy = 'num_days';
|
||||
} elseif ($name == 'fonts') {
|
||||
$orderBy = 'sort_order';
|
||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
||||
$orderBy = 'name';
|
||||
} else {
|
||||
$orderBy = 'id';
|
||||
}
|
||||
$tableData = $class::orderBy($orderBy)->get();
|
||||
if ($tableData->count()) {
|
||||
Cache::forever($name, $tableData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Migrating');
|
||||
Artisan::call('migrate:fresh --force');
|
||||
|
||||
$this->info('Seeding');
|
||||
Artisan::call('db:seed --force');
|
||||
|
||||
$this->buildCache(true);
|
||||
|
||||
$this->info('Seeding Random Data');
|
||||
$this->createSmallAccount();
|
||||
|
||||
|
@ -185,7 +185,7 @@ class InvoiceFilters extends QueryFilters
|
||||
* @param string sort formatted as column|asc
|
||||
* @return Builder
|
||||
*/
|
||||
public function sort(string $sort) : Builder
|
||||
public function sort(string $sort = '') : Builder
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
|
@ -539,7 +539,7 @@ class BaseController extends Controller
|
||||
$query->where('bank_integrations.user_id', $user->id);
|
||||
}
|
||||
},
|
||||
'company.bank_transaction_rules'=> function ($query) use ($updated_at, $user) {
|
||||
'company.bank_transaction_rules'=> function ($query) use ($user) {
|
||||
|
||||
if (! $user->isAdmin()) {
|
||||
$query->where('bank_transaction_rules.user_id', $user->id);
|
||||
|
@ -44,7 +44,7 @@ class StoreBankIntegrationRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if(!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0)
|
||||
if(!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0 && array_key_exists('bank_account_name', $input))
|
||||
$input['provider_name'] = $input['bank_account_name'];
|
||||
|
||||
$this->replace($input);
|
||||
|
@ -15,6 +15,7 @@ use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
|
||||
use App\Models\Gateway;
|
||||
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreCompanyGatewayRequest extends Request
|
||||
{
|
||||
@ -33,7 +34,7 @@ class StoreCompanyGatewayRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'gateway_key' => 'required|alpha_num',
|
||||
'gateway_key' => ['required','alpha_num',Rule::exists('gateways','key')],
|
||||
'fees_and_limits' => new ValidCompanyGatewayFeesAndLimitsRule(),
|
||||
];
|
||||
|
||||
|
@ -62,8 +62,15 @@ class AutoBillCron
|
||||
|
||||
nlog($auto_bill_partial_invoices->count().' partial invoices to auto bill');
|
||||
|
||||
$auto_bill_partial_invoices->cursor()->each(function ($invoice) {
|
||||
AutoBill::dispatch($invoice->id, false);
|
||||
$auto_bill_partial_invoices->chunk(100, function ($invoices) {
|
||||
|
||||
foreach($invoices as $invoice)
|
||||
{
|
||||
AutoBill::dispatch($invoice->id, false);
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
|
||||
});
|
||||
|
||||
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
|
||||
@ -79,8 +86,15 @@ class AutoBillCron
|
||||
|
||||
nlog($auto_bill_invoices->count().' full invoices to auto bill');
|
||||
|
||||
$auto_bill_invoices->cursor()->each(function ($invoice) {
|
||||
AutoBill::dispatch($invoice->id, false);
|
||||
$auto_bill_invoices->chunk(100, function ($invoices) {
|
||||
|
||||
foreach($invoices as $invoice)
|
||||
{
|
||||
AutoBill::dispatch($invoice->id, false);
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
|
||||
});
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
@ -100,8 +114,14 @@ class AutoBillCron
|
||||
|
||||
nlog($auto_bill_partial_invoices->count()." partial invoices to auto bill db = {$db}");
|
||||
|
||||
$auto_bill_partial_invoices->cursor()->each(function ($invoice) use ($db) {
|
||||
AutoBill::dispatch($invoice->id, $db);
|
||||
$auto_bill_partial_invoices->chunk(100, function ($invoices) use($db){
|
||||
|
||||
foreach($invoices as $invoice)
|
||||
{
|
||||
AutoBill::dispatch($invoice->id, $db);
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
});
|
||||
|
||||
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
|
||||
@ -117,10 +137,15 @@ class AutoBillCron
|
||||
|
||||
nlog($auto_bill_invoices->count()." full invoices to auto bill db = {$db}");
|
||||
|
||||
$auto_bill_invoices->cursor()->each(function ($invoice) use ($db) {
|
||||
nlog($this->counter);
|
||||
AutoBill::dispatch($invoice->id, $db);
|
||||
$this->counter++;
|
||||
$auto_bill_invoices->chunk(100, function ($invoices) use($db){
|
||||
|
||||
foreach($invoices as $invoice)
|
||||
{
|
||||
AutoBill::dispatch($invoice->id, $db);
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ class ReminderJob implements ShouldQueue
|
||||
(Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
|
||||
|
||||
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template)->delay(now()->addSeconds(3));
|
||||
nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
|
||||
});
|
||||
|
||||
|
@ -29,8 +29,6 @@ class InvoiceFailedEmailNotification
|
||||
|
||||
use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $delay = 10;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
@ -331,7 +331,7 @@ class ACH
|
||||
$data = [
|
||||
'gateway_type_id' => $cgt->gateway_type_id,
|
||||
'payment_type' => PaymentType::ACH,
|
||||
'transaction_reference' => $response->charges->data[0]->id,
|
||||
'transaction_reference' => isset($response->latest_charge) ? $response->latest_charge : $response->charges->data[0]->id,
|
||||
'amount' => $amount,
|
||||
];
|
||||
|
||||
|
@ -135,7 +135,7 @@ class BrowserPay implements MethodInterface
|
||||
'payment_method' => $gateway_response->payment_method,
|
||||
'payment_type' => PaymentType::parseCardType(strtolower($payment_method->card->brand)),
|
||||
'amount' => $this->stripe->convertFromStripeAmount($gateway_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
|
||||
'transaction_reference' => optional($payment_intent->charges->data[0])->id,
|
||||
'transaction_reference' => isset($payment_intent->latest_charge) ? $payment_intent->latest_charge : $payment_intent->charges->data[0]->id,
|
||||
'gateway_type_id' => GatewayType::APPLE_PAY,
|
||||
];
|
||||
|
||||
|
@ -141,20 +141,27 @@ class Charge
|
||||
$payment_method_type = PaymentType::SEPA;
|
||||
$status = Payment::STATUS_PENDING;
|
||||
} else {
|
||||
$payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
|
||||
|
||||
if(isset($response->latest_charge)) {
|
||||
$charge = \Stripe\Charge::retrieve($response->latest_charge, $this->stripe->stripe_connect_auth);
|
||||
$payment_method_type = $charge->payment_method_details->card->brand;
|
||||
}
|
||||
elseif(isset($response->charges->data[0]->payment_method_details->card->brand))
|
||||
$payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
|
||||
else
|
||||
$payment_method_type = 'visa';
|
||||
|
||||
$status = Payment::STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
if($response?->status == 'processing'){
|
||||
//allows us to jump over the next stage - used for SEPA
|
||||
}elseif($response?->status != 'succeeded'){
|
||||
|
||||
if(!in_array($response?->status, ['succeeded', 'processing'])){
|
||||
$this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
|
||||
}
|
||||
|
||||
$data = [
|
||||
'gateway_type_id' => $cgt->gateway_type_id,
|
||||
'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type),
|
||||
'transaction_reference' => $response->charges->data[0]->id,
|
||||
'transaction_reference' => isset($response->latest_charge) ? $response->latest_charge : $response->charges->data[0]->id,
|
||||
'amount' => $amount,
|
||||
];
|
||||
|
||||
@ -162,6 +169,7 @@ class Charge
|
||||
$payment->meta = $cgt->meta;
|
||||
$payment->save();
|
||||
|
||||
$payment_hash->data = array_merge((array) $payment_hash->data, ['payment_intent' => $response, 'amount_with_fee' => $amount]);
|
||||
$payment_hash->payment_id = $payment->id;
|
||||
$payment_hash->save();
|
||||
|
||||
|
@ -267,6 +267,39 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
// private function updateSepaPayment($payment_hash, $client, $meta)
|
||||
// {
|
||||
|
||||
// $company_gateway = CompanyGateway::find($this->company_gateway_id);
|
||||
// $payment_method_type = GatewayType::SEPA;
|
||||
// $driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);
|
||||
|
||||
// $payment_hash->data = array_merge((array) $payment_hash->data, $this->stripe_request);
|
||||
// $payment_hash->save();
|
||||
// $driver->setPaymentHash($payment_hash);
|
||||
|
||||
// $data = [
|
||||
// 'payment_method' => $payment_hash->data->object->payment_method,
|
||||
// 'payment_type' => PaymentType::parseCardType(strtolower($meta['card_details'])) ?: PaymentType::CREDIT_CARD_OTHER,
|
||||
// 'amount' => $payment_hash->data->amount_with_fee,
|
||||
// 'transaction_reference' => $meta['transaction_reference'],
|
||||
// 'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
// ];
|
||||
|
||||
// $payment = $driver->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
// SystemLogger::dispatch(
|
||||
// ['response' => $this->stripe_request, 'data' => $data],
|
||||
// SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
// SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
// SystemLog::TYPE_STRIPE,
|
||||
// $client,
|
||||
// $client->company,
|
||||
// );
|
||||
|
||||
|
||||
// }
|
||||
|
||||
|
||||
private function updateCreditCardPayment($payment_hash, $client, $meta)
|
||||
{
|
||||
|
@ -660,14 +660,22 @@ class StripePaymentDriver extends BaseDriver
|
||||
], $this->stripe_connect_auth);
|
||||
|
||||
if ($charge->captured) {
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $transaction['payment_intent'])
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->where(function ($query) use ($transaction) {
|
||||
$query->where('transaction_reference', $transaction['payment_intent'])
|
||||
->orWhere('transaction_reference', $transaction['id']);
|
||||
})
|
||||
->first();
|
||||
|
||||
$payment = false;
|
||||
|
||||
if(isset($transaction['payment_intent']))
|
||||
{
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $transaction['payment_intent'])
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->first();
|
||||
}
|
||||
elseif(isset($transaction['id'])) {
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->first();
|
||||
}
|
||||
|
||||
if ($payment) {
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
|
@ -31,32 +31,16 @@ use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class BankMatchingService implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesCounter;
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private Company $company;
|
||||
public function __construct(protected int $company_id, private string $db){}
|
||||
|
||||
private $invoices;
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public function __construct(private int $company_id, private string $db){}
|
||||
|
||||
public function handle()
|
||||
public function handle() :void
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->company = Company::find($this->company_id);
|
||||
|
||||
$this->matchTransactions();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function matchTransactions()
|
||||
{
|
||||
|
||||
BankTransaction::where('company_id', $this->company->id)
|
||||
BankTransaction::where('company_id', $this->company_id)
|
||||
->where('status_id', BankTransaction::STATUS_UNMATCHED)
|
||||
->cursor()
|
||||
->each(function ($bt){
|
||||
@ -64,11 +48,11 @@ class BankMatchingService implements ShouldQueue
|
||||
(new BankService($bt))->processRules();
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [new WithoutOverlapping($this->company_id)];
|
||||
return [new WithoutOverlapping("bank_match_rate:{$this->company_id}")];
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class SendEmail
|
||||
|
||||
$this->credit->invitations->each(function ($invitation) {
|
||||
if (! $invitation->contact->trashed() && $invitation->contact->email) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template);
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template)->delay(2);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -112,8 +112,7 @@ class AddGatewayFee extends AbstractService
|
||||
$this->invoice
|
||||
->client
|
||||
->service()
|
||||
->updateBalance($adjustment)
|
||||
->save();
|
||||
->updateBalance($adjustment);
|
||||
|
||||
$this->invoice
|
||||
->ledger()
|
||||
|
@ -36,7 +36,7 @@ class LedgerService
|
||||
|
||||
$this->entity->company_ledger()->save($company_ledger);
|
||||
|
||||
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300));
|
||||
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -52,7 +52,7 @@ class LedgerService
|
||||
|
||||
$this->entity->company_ledger()->save($company_ledger);
|
||||
|
||||
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300));
|
||||
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -68,7 +68,7 @@ class LedgerService
|
||||
|
||||
$this->entity->company_ledger()->save($company_ledger);
|
||||
|
||||
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300));
|
||||
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class SendEmail
|
||||
$contact = $this->payment->client->contacts()->first();
|
||||
|
||||
if ($contact?->email)
|
||||
EmailPayment::dispatch($this->payment, $this->payment->company, $contact);
|
||||
EmailPayment::dispatch($this->payment, $this->payment->company, $contact)->delay(now()->addSeconds(3));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ trait Inviteable
|
||||
|
||||
$qr = $writer->writeString($this->getPaymentLink(), 'utf-8');
|
||||
|
||||
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
|
||||
return "<svg class='pqrcode' viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
|
||||
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
|
||||
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.5.42',
|
||||
'app_tag' => '5.5.42',
|
||||
'app_version' => '5.5.43',
|
||||
'app_tag' => '5.5.43',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -4,9 +4,12 @@ use App\Models\Currency;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
use AppSetup;
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
@ -65,6 +68,9 @@ return new class extends Migration
|
||||
|
||||
\Illuminate\Support\Facades\Artisan::call('ninja:design-update');
|
||||
|
||||
$this->buildCache(true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user