mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge branch 'v5-develop' into yodlee
This commit is contained in:
commit
0a2cb6f88d
@ -1 +1 @@
|
||||
5.5.19
|
||||
5.5.27
|
@ -20,6 +20,7 @@ use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Credit;
|
||||
use App\Models\CreditInvitation;
|
||||
@ -702,13 +703,23 @@ class CheckData extends Command
|
||||
->count();
|
||||
|
||||
if($count == 0){
|
||||
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be 0");
|
||||
|
||||
//factor in over payments to the client balance
|
||||
$over_payment = Payment::where('client_id', $client->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [1,4])
|
||||
->selectRaw('sum(amount - applied) as p')
|
||||
->pluck('p')
|
||||
->first();
|
||||
|
||||
if($this->option('client_balance')){
|
||||
|
||||
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be {$over_payment}");
|
||||
|
||||
if($this->option('client_balance') && (floatval($over_payment) != floatval($client->balance) )){
|
||||
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to 0");
|
||||
|
||||
$client->balance = 0;
|
||||
$client->balance = $over_payment * -1;
|
||||
$client->save();
|
||||
|
||||
}
|
||||
|
59
app/DataMapper/Analytics/EmailCount.php
Normal file
59
app/DataMapper/Analytics/EmailCount.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Analytics;
|
||||
|
||||
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
|
||||
|
||||
class EmailCount extends GenericMixedMetric
|
||||
{
|
||||
/**
|
||||
* The type of Sample.
|
||||
*
|
||||
* Monotonically incrementing counter
|
||||
*
|
||||
* - counter
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'mixed_metric';
|
||||
|
||||
/**
|
||||
* The name of the counter.
|
||||
* @var string
|
||||
*/
|
||||
public $name = 'account.daily_email_count';
|
||||
|
||||
/**
|
||||
* The datetime of the counter measurement.
|
||||
*
|
||||
* date("Y-m-d H:i:s")
|
||||
*
|
||||
* @var DateTime
|
||||
*/
|
||||
public $datetime;
|
||||
|
||||
/**
|
||||
* The Class failure name
|
||||
* set to 0.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $string_metric5 = 'account_key';
|
||||
|
||||
public $int_metric1 = 1;
|
||||
|
||||
public function __construct($int_metric1, $string_metric5)
|
||||
{
|
||||
$this->int_metric1 = $int_metric1;
|
||||
$this->string_metric5 = $string_metric5;
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ class CompanySettings extends BaseSettings
|
||||
public $auto_archive_invoice = false; // @implemented
|
||||
|
||||
public $qr_iban = ''; //@implemented
|
||||
|
||||
public $besr_id = ''; //@implemented
|
||||
|
||||
public $lock_invoices = 'off'; //off,when_sent,when_paid //@implemented
|
||||
|
@ -44,6 +44,10 @@ class RecurringExpenseToExpenseFactory
|
||||
$expense->payment_date = $recurring_expense->payment_date;
|
||||
$expense->amount = $recurring_expense->amount;
|
||||
$expense->foreign_amount = $recurring_expense->foreign_amount ?: 0;
|
||||
|
||||
//11-09-2022 - we should be tracking the recurring expense!!
|
||||
$expense->recurring_expense_id = $recurring_expense->id;
|
||||
|
||||
// $expense->private_notes = $recurring_expense->private_notes;
|
||||
// $expense->public_notes = $recurring_expense->public_notes;
|
||||
|
||||
|
@ -29,10 +29,7 @@ class RecurringInvoiceToInvoiceFactory
|
||||
$invoice->terms = self::tranformObject($recurring_invoice->terms, $client);
|
||||
$invoice->public_notes = self::tranformObject($recurring_invoice->public_notes, $client);
|
||||
$invoice->private_notes = $recurring_invoice->private_notes;
|
||||
//$invoice->date = now()->format($client->date_format());
|
||||
//$invoice->due_date = $recurring_invoice->calculateDueDate(now());
|
||||
$invoice->is_deleted = $recurring_invoice->is_deleted;
|
||||
// $invoice->line_items = $recurring_invoice->line_items;
|
||||
$invoice->line_items = self::transformItems($recurring_invoice, $client);
|
||||
$invoice->tax_name1 = $recurring_invoice->tax_name1;
|
||||
$invoice->tax_rate1 = $recurring_invoice->tax_rate1;
|
||||
|
@ -30,7 +30,10 @@ class ClientFilters extends QueryFilters
|
||||
*/
|
||||
public function name(string $name): Builder
|
||||
{
|
||||
return $this->builder->where('name', 'like', '%'.$name.'%');
|
||||
if(strlen($name) >=1)
|
||||
return $this->builder->where('name', 'like', '%'.$name.'%');
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ class SwissQrGenerator
|
||||
$this->client->address1 ? substr($this->client->address1, 0 , 70) : '',
|
||||
$this->client->address2 ? substr($this->client->address2, 0 , 16) : '',
|
||||
$this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '',
|
||||
$this->client->city ? substr($this->client->postal_code, 0, 35) : '',
|
||||
$this->client->city ? substr($this->client->city, 0, 35) : '',
|
||||
'CH'
|
||||
));
|
||||
|
||||
@ -104,16 +104,39 @@ class SwissQrGenerator
|
||||
|
||||
// Add payment reference
|
||||
// This is what you will need to identify incoming payments.
|
||||
$referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(
|
||||
$this->company->present()->besr_id() ?: '', // You receive this number from your bank (BESR-ID). Unless your bank is PostFinance, in that case use NULL.
|
||||
$this->invoice->number// A number to match the payment with your internal data, e.g. an invoice number
|
||||
);
|
||||
|
||||
$qrBill->setPaymentReference(
|
||||
QrBill\DataGroup\Element\PaymentReference::create(
|
||||
QrBill\DataGroup\Element\PaymentReference::TYPE_QR,
|
||||
$referenceNumber
|
||||
));
|
||||
if(stripos($this->invoice->number, "Live-") === 0)
|
||||
{
|
||||
// we're currently in preview status. Let's give a dummy reference for now
|
||||
$invoice_number = "123456789";
|
||||
}
|
||||
else
|
||||
{
|
||||
$invoice_number = $this->invoice->number;
|
||||
}
|
||||
|
||||
if(strlen($this->company->present()->besr_id()) > 1)
|
||||
{
|
||||
$referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate(
|
||||
$this->company->present()->besr_id() ?: '', // You receive this number from your bank (BESR-ID). Unless your bank is PostFinance, in that case use NULL.
|
||||
$invoice_number// A number to match the payment with your internal data, e.g. an invoice number
|
||||
);
|
||||
|
||||
$qrBill->setPaymentReference(
|
||||
QrBill\DataGroup\Element\PaymentReference::create(
|
||||
QrBill\DataGroup\Element\PaymentReference::TYPE_QR,
|
||||
$referenceNumber
|
||||
));
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
$qrBill->setPaymentReference(
|
||||
QrBill\DataGroup\Element\PaymentReference::create(
|
||||
QrBill\DataGroup\Element\PaymentReference::TYPE_NON
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
// Optionally, add some human-readable information about what the bill is for.
|
||||
$qrBill->setAdditionalInformation(
|
||||
@ -141,6 +164,8 @@ class SwissQrGenerator
|
||||
nlog($violation);
|
||||
}
|
||||
|
||||
nlog($e->getMessage());
|
||||
|
||||
return '';
|
||||
// return $e->getMessage();
|
||||
}
|
||||
@ -148,4 +173,4 @@ class SwissQrGenerator
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class ActivityController extends BaseController
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/actvities",
|
||||
* path="/api/v1/activities",
|
||||
* operationId="getActivities",
|
||||
* tags={"actvities"},
|
||||
* summary="Gets a list of actvities",
|
||||
|
@ -810,8 +810,16 @@ class BaseController extends Controller
|
||||
// 10-01-2022 need to ensure we snake case properly here to ensure permissions work as expected
|
||||
// 28-03-2022 this is definitely correct here, do not append _ to the view, it resolved correctly when snake cased
|
||||
if (auth()->user() && ! auth()->user()->hasPermission('view'.lcfirst(class_basename(Str::snake($this->entity_type))))) {
|
||||
//03-09-2022
|
||||
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
|
||||
|
||||
//06-10-2022 - some entities do not have assigned_user_id - this becomes an issue when we have a large company and low permission users
|
||||
if(lcfirst(class_basename(Str::snake($this->entity_type))) == 'user')
|
||||
$query->where('id', auth()->user()->id);
|
||||
elseif(in_array(lcfirst(class_basename(Str::snake($this->entity_type))),['design','group_setting','payment_term'])){
|
||||
//need to pass these back regardless
|
||||
}
|
||||
else
|
||||
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
|
||||
|
||||
}
|
||||
|
||||
if (request()->has('updated_at') && request()->input('updated_at') > 0) {
|
||||
|
@ -56,7 +56,7 @@ class InvoiceController extends Controller
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
$invoice->service()->removeUnpaidGatewayFees()->save();
|
||||
// $invoice->service()->removeUnpaidGatewayFees()->save();
|
||||
|
||||
$invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
|
||||
|
||||
|
@ -179,7 +179,7 @@ class NinjaPlanController extends Controller
|
||||
->queue();
|
||||
|
||||
$ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id'));
|
||||
$ninja_company->notification(new NewAccountNotification($account, $client))->ninja();
|
||||
$ninja_company->notification(new NewAccountNotification($subscription->company->account, $client))->ninja();
|
||||
|
||||
return $this->render('plan.trial_confirmed', $data);
|
||||
}
|
||||
|
@ -131,8 +131,10 @@ class EmailController extends BaseController
|
||||
if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified)
|
||||
return response(['message' => 'Please verify your account to send emails.'], 400);
|
||||
|
||||
if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order'){
|
||||
return $this->sendPurchaseOrder($entity_obj, $data);
|
||||
nlog($entity);
|
||||
|
||||
if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder'){
|
||||
return $this->sendPurchaseOrder($entity_obj, $data, $template);
|
||||
}
|
||||
|
||||
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_string, $entity_obj, $template) {
|
||||
@ -183,13 +185,15 @@ class EmailController extends BaseController
|
||||
return $this->itemResponse($entity_obj->fresh());
|
||||
}
|
||||
|
||||
private function sendPurchaseOrder($entity_obj, $data)
|
||||
private function sendPurchaseOrder($entity_obj, $data, $template)
|
||||
{
|
||||
|
||||
$this->entity_type = PurchaseOrder::class;
|
||||
|
||||
$this->entity_transformer = PurchaseOrderTransformer::class;
|
||||
|
||||
$data['template'] = $template;
|
||||
|
||||
PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data);
|
||||
|
||||
return $this->itemResponse($entity_obj);
|
||||
|
@ -578,6 +578,16 @@ class InvoiceController extends BaseController
|
||||
return response()->json(['message' => ctrans('texts.sent_message')], 200);
|
||||
}
|
||||
|
||||
if($action == 'download' && $invoices->count() >=1 && auth()->user()->can('view', $invoices->first())) {
|
||||
|
||||
$file = $invoices->first()->service()->getInvoicePdf();
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the other actions to the switch
|
||||
*/
|
||||
|
@ -306,7 +306,7 @@ class PreviewController extends BaseController
|
||||
if (Ninja::isHosted()) {
|
||||
LightLogs::create(new LivePreview())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
}
|
||||
|
||||
$response = Response::make($file_path, 200);
|
||||
|
@ -292,7 +292,7 @@ class PreviewPurchaseOrderController extends BaseController
|
||||
{
|
||||
LightLogs::create(new LivePreview())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
}
|
||||
|
||||
|
||||
|
@ -210,7 +210,7 @@ class RecurringInvoiceController extends BaseController
|
||||
|
||||
event(new RecurringInvoiceWasCreated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($recurring_invoice);
|
||||
return $this->itemResponse($recurring_invoice->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,39 +69,7 @@ class SelfUpdateController extends BaseController
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
// public function old_update(\Codedge\Updater\UpdaterManager $updater)
|
||||
// {
|
||||
// set_time_limit(0);
|
||||
// define('STDIN', fopen('php://stdin', 'r'));
|
||||
|
||||
// if (Ninja::isHosted()) {
|
||||
// return response()->json(['message' => ctrans('texts.self_update_not_available')], 403);
|
||||
// }
|
||||
|
||||
// $this->testWritable();
|
||||
|
||||
// // Get the new version available
|
||||
// $versionAvailable = $updater->source()->getVersionAvailable();
|
||||
|
||||
// // Create a release
|
||||
// $release = $updater->source()->fetch($versionAvailable);
|
||||
|
||||
// $updater->source()->update($release);
|
||||
|
||||
// $cacheCompiled = base_path('bootstrap/cache/compiled.php');
|
||||
// if (file_exists($cacheCompiled)) { unlink ($cacheCompiled); }
|
||||
// $cacheServices = base_path('bootstrap/cache/services.php');
|
||||
// if (file_exists($cacheServices)) { unlink ($cacheServices); }
|
||||
|
||||
// Artisan::call('clear-compiled');
|
||||
// Artisan::call('route:clear');
|
||||
// Artisan::call('view:clear');
|
||||
// Artisan::call('optimize');
|
||||
|
||||
// return response()->json(['message' => 'Update completed'], 200);
|
||||
|
||||
// }
|
||||
|
||||
|
||||
public function update()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
@ -40,7 +40,10 @@ class WePayController extends BaseController
|
||||
$company = Company::where('company_key', $hash['company_key'])->firstOrFail();
|
||||
|
||||
$data['user_id'] = $user->id;
|
||||
$data['company'] = $company;
|
||||
$data['user_company'] = $company;
|
||||
|
||||
// $data['company_key'] = $company->company_key;
|
||||
// $data['db'] = $company->db;
|
||||
|
||||
$wepay_driver = new WePayPaymentDriver(new CompanyGateway, null, null);
|
||||
|
||||
|
@ -98,8 +98,6 @@ class Kernel extends HttpKernel
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// 'throttle:300,1',
|
||||
// 'cors',
|
||||
'bindings',
|
||||
'query_logging',
|
||||
],
|
||||
|
@ -35,7 +35,7 @@ class CreditsTable extends Component
|
||||
public function render()
|
||||
{
|
||||
$query = Credit::query()
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->where('company_id', $this->company->id)
|
||||
->where('status_id', '<>', Credit::STATUS_DRAFT)
|
||||
->where('is_deleted', 0)
|
||||
|
@ -43,10 +43,10 @@ class InvoicesTable extends Component
|
||||
$local_status = [];
|
||||
|
||||
$query = Invoice::query()
|
||||
->with('client.gateway_tokens', 'client.contacts')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', false);
|
||||
->where('is_deleted', false)
|
||||
->with('client.gateway_tokens', 'client.contacts')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
|
||||
if (in_array('paid', $this->status)) {
|
||||
$local_status[] = Invoice::STATUS_PAID;
|
||||
|
@ -40,9 +40,9 @@ class PaymentsTable extends Component
|
||||
{
|
||||
$query = Payment::query()
|
||||
->with('type', 'client')
|
||||
->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
@ -45,10 +45,10 @@ class PurchaseOrdersTable extends Component
|
||||
|
||||
$query = PurchaseOrder::query()
|
||||
->with('vendor.contacts')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->whereIn('status_id', [PurchaseOrder::STATUS_SENT, PurchaseOrder::STATUS_ACCEPTED])
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', false);
|
||||
->whereIn('status_id', [PurchaseOrder::STATUS_SENT, PurchaseOrder::STATUS_ACCEPTED])
|
||||
->where('is_deleted', false)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
|
||||
if (in_array('sent', $this->status)) {
|
||||
$local_status[] = PurchaseOrder::STATUS_SENT;
|
||||
|
@ -79,9 +79,9 @@ class QuotesTable extends Component
|
||||
|
||||
$query = $query
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
->where('status_id', '<>', Quote::STATUS_DRAFT)
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->where('is_deleted', 0)
|
||||
->where('status_id', '<>', Quote::STATUS_DRAFT)
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
@ -40,7 +40,7 @@ class RecurringInvoicesTable extends Component
|
||||
$query = RecurringInvoice::query();
|
||||
|
||||
$query = $query
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->where('company_id', $this->company->id)
|
||||
->whereIn('status_id', [RecurringInvoice::STATUS_ACTIVE])
|
||||
->orderBy('status_id', 'asc')
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\CompanyGateway;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
@ -105,6 +106,8 @@ class RequiredClientInfo extends Component
|
||||
|
||||
public $company;
|
||||
|
||||
public $company_gateway_id;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
@ -141,6 +144,8 @@ class RequiredClientInfo extends Component
|
||||
'client_postal_code' => $this->contact->client->postal_code,
|
||||
]);
|
||||
|
||||
//if stripe is enabled, we want to update the customer at this point.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -150,6 +155,7 @@ class RequiredClientInfo extends Component
|
||||
|
||||
private function updateClientDetails(array $data): bool
|
||||
{
|
||||
nlog($this->company->id);
|
||||
$client = [];
|
||||
$contact = [];
|
||||
|
||||
@ -172,6 +178,16 @@ class RequiredClientInfo extends Component
|
||||
->push();
|
||||
|
||||
if ($contact_update && $client_update) {
|
||||
|
||||
$cg = CompanyGateway::find($this->company_gateway_id);
|
||||
|
||||
if($cg && $cg->update_details){
|
||||
$payment_gateway = $cg->driver($this->client)->init();
|
||||
|
||||
if(method_exists($payment_gateway, "updateCustomer"))
|
||||
$payment_gateway->updateCustomer();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ class TasksTable extends Component
|
||||
$query = Task::query()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id);
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id);
|
||||
|
||||
if ($this->company->getSetting('show_all_tasks_client_portal') === 'invoiced') {
|
||||
$query = $query->whereNotNull('invoice_id');
|
||||
|
@ -69,8 +69,8 @@ class QueryLogging
|
||||
$ip = request()->ip();
|
||||
}
|
||||
|
||||
LightLogs::create(new DbQuery($request->method(), urldecode($request->url()), $count, $time, $ip))
|
||||
->queue();
|
||||
LightLogs::create(new DbQuery($request->method(), substr(urldecode($request->url()),0,180), $count, $time, $ip))
|
||||
->batch();
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
@ -158,6 +158,10 @@ class StoreClientRequest extends Request
|
||||
unset($input['number']);
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $input)) {
|
||||
$input['name'] = strip_tags($input['name']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -112,6 +112,10 @@ class UpdateClientRequest extends Request
|
||||
$input['settings'] = $this->filterSaveableSettings($input['settings']);
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $input)) {
|
||||
$input['name'] = strip_tags($input['name']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -65,15 +65,15 @@ class StoreCompanyRequest extends Request
|
||||
$input['google_analytics_key'] = $input['google_analytics_url'];
|
||||
}
|
||||
|
||||
$company_settings = CompanySettings::defaults();
|
||||
// $company_settings = CompanySettings::defaults();
|
||||
|
||||
//@todo this code doesn't make sense as we never return $company_settings anywhere
|
||||
//@deprecated???
|
||||
if (array_key_exists('settings', $input) && ! empty($input['settings'])) {
|
||||
foreach ($input['settings'] as $key => $value) {
|
||||
$company_settings->{$key} = $value;
|
||||
}
|
||||
}
|
||||
// if (array_key_exists('settings', $input) && ! empty($input['settings'])) {
|
||||
// foreach ($input['settings'] as $key => $value) {
|
||||
// $company_settings->{$key} = $value;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (array_key_exists('portal_domain', $input)) {
|
||||
$input['portal_domain'] = strtolower($input['portal_domain']);
|
||||
|
@ -49,7 +49,7 @@ class StoreGroupSettingRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
$input['settings'] = $group_settings;
|
||||
$input['settings'] = (array)$group_settings;
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -73,6 +73,6 @@ class UpdateGroupSettingRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
return (array)$settings;
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ class StoreInvoiceRequest extends Request
|
||||
|
||||
$rules['invitations.*.client_contact_id'] = 'distinct';
|
||||
|
||||
$rules['number'] = ['nullable', Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)];
|
||||
$rules['number'] = ['bail', 'nullable', Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)];
|
||||
|
||||
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
|
||||
$rules['is_amount_discount'] = ['boolean'];
|
||||
@ -95,6 +95,9 @@ class StoreInvoiceRequest extends Request
|
||||
if (array_key_exists('tax_rate3', $input) && is_null($input['tax_rate3'])) {
|
||||
$input['tax_rate3'] = 0;
|
||||
}
|
||||
if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) {
|
||||
$input['exchange_rate'] = 1;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ class StorePaymentRequest extends Request
|
||||
'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())],
|
||||
'invoices' => new ValidPayableInvoicesRule(),
|
||||
'number' => ['nullable', 'bail', Rule::unique('payments')->where('company_id', auth()->user()->company()->id)],
|
||||
'idempotency_key' => ['nullable', 'bail', 'string','max:64', Rule::unique('payments')->where('company_id', auth()->user()->company()->id)],
|
||||
|
||||
];
|
||||
|
||||
|
@ -85,6 +85,14 @@ class StoreUserRequest extends Request
|
||||
];
|
||||
}
|
||||
|
||||
if (array_key_exists('first_name', $input)) {
|
||||
$input['first_name'] = strip_tags($input['first_name']);
|
||||
}
|
||||
|
||||
if (array_key_exists('last_name', $input)) {
|
||||
$input['last_name'] = strip_tags($input['last_name']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,14 @@ class UpdateUserRequest extends Request
|
||||
$input['email'] = trim($input['email']);
|
||||
}
|
||||
|
||||
if (array_key_exists('first_name', $input)) {
|
||||
$input['first_name'] = strip_tags($input['first_name']);
|
||||
}
|
||||
|
||||
if (array_key_exists('last_name', $input)) {
|
||||
$input['last_name'] = strip_tags($input['last_name']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class BlackListRule implements Rule
|
||||
'arxxwalls.com',
|
||||
'superhostforumla.com',
|
||||
'wnpop.com',
|
||||
'dataservices.space',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,15 @@ class ExpenseMap
|
||||
9 => 'expense.transaction_reference',
|
||||
10 => 'expense.public_notes',
|
||||
11 => 'expense.private_notes',
|
||||
12 => 'expense.tax_name1',
|
||||
13 => 'expense.tax_rate1',
|
||||
14 => 'expense.tax_name2',
|
||||
15 => 'expense.tax_rate2',
|
||||
16 => 'expense.tax_name3',
|
||||
17 => 'expense.tax_rate3',
|
||||
18 => 'expense.uses_inclusive_taxes',
|
||||
19 => 'expense.payment_date',
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -46,6 +55,15 @@ class ExpenseMap
|
||||
9 => 'texts.transaction_reference',
|
||||
10 => 'texts.public_notes',
|
||||
11 => 'texts.private_notes',
|
||||
12 => 'texts.tax_name1',
|
||||
13 => 'texts.tax_rate1',
|
||||
14 => 'texts.tax_name2',
|
||||
15 => 'texts.tax_rate2',
|
||||
16 => 'texts.tax_name3',
|
||||
17 => 'texts.tax_rate3',
|
||||
18 => 'texts.uses_inclusive_taxes',
|
||||
19 => 'texts.payment_date',
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -173,18 +173,18 @@ class BaseImport
|
||||
$is_free_hosted_client = $this->company->account->isFreeHostedClient();
|
||||
$hosted_client_count = $this->company->account->hosted_client_count;
|
||||
|
||||
if($this->factory_name == 'App\Factory\ClientFactory' && $is_free_hosted_client && (count($data) > $hosted_client_count))
|
||||
{
|
||||
$this->error_array[$entity_type][] = [
|
||||
$entity_type => 'client',
|
||||
'error' => 'Error, you are attempting to import more clients than your plan allows',
|
||||
];
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
foreach ($data as $key => $record) {
|
||||
|
||||
if($this->factory_name instanceof ClientFactory && $is_free_hosted_client && ($this->company->clients()->count() > $hosted_client_count))
|
||||
{
|
||||
$this->error_array[$entity_type][] = [
|
||||
$entity_type => $record,
|
||||
'error' => 'Client limit reached',
|
||||
];
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
try {
|
||||
$entity = $this->transformer->transform($record);
|
||||
// $validator = $this->request_name::runFormRequest($entity);
|
||||
|
@ -178,12 +178,14 @@ class BaseTransformer
|
||||
public function getFloat($data, $field)
|
||||
{
|
||||
if (array_key_exists($field, $data)) {
|
||||
$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
||||
//$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
||||
return Number::parseStringFloat($data[$field]);
|
||||
} else {
|
||||
$number = 0;
|
||||
//$number = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Number::parseFloat($number);
|
||||
// return Number::parseFloat($number);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,7 @@ class ExpenseTransformer extends BaseTransformer
|
||||
'client_id' => isset($data['expense.client'])
|
||||
? $this->getClientId($data['expense.client'])
|
||||
: null,
|
||||
'date' => strlen($this->getString($data, 'expense.date') > 1) ? date('Y-m-d', strtotime($this->getString($data, 'expense.date'))) : now()->format('Y-m-d'),
|
||||
'date' => strlen($this->getString($data, 'expense.date') > 1) ? date('Y-m-d', strtotime(str_replace("/","-",$data['expense.date']))) : now()->format('Y-m-d'),
|
||||
'public_notes' => $this->getString($data, 'expense.public_notes'),
|
||||
'private_notes' => $this->getString($data, 'expense.private_notes'),
|
||||
'category_id' => isset($data['expense.category'])
|
||||
@ -55,7 +55,7 @@ class ExpenseTransformer extends BaseTransformer
|
||||
? $this->getPaymentTypeId($data['expense.payment_type'])
|
||||
: null,
|
||||
'payment_date' => isset($data['expense.payment_date'])
|
||||
? date('Y-m-d', strtotime($data['expense.payment_date']))
|
||||
? date('Y-m-d', strtotime(str_replace("/","-",$data['expense.payment_date'])))
|
||||
: null,
|
||||
'custom_value1' => $this->getString($data, 'expense.custom_value1'),
|
||||
'custom_value2' => $this->getString($data, 'expense.custom_value2'),
|
||||
@ -66,6 +66,14 @@ class ExpenseTransformer extends BaseTransformer
|
||||
'expense.transaction_reference'
|
||||
),
|
||||
'should_be_invoiced' => $clientId ? true : false,
|
||||
'uses_inclusive_taxes' => (bool) $this->getString($data, 'expense.uses_inclusive_taxes'),
|
||||
'tax_name1' => $this->getString($data, 'expense.tax_name1'),
|
||||
'tax_rate1' => $this->getFloat($data, 'expense.tax_rate1'),
|
||||
'tax_name2' => $this->getString($data, 'expense.tax_name2'),
|
||||
'tax_rate2' => $this->getFloat($data, 'expense.tax_rate2'),
|
||||
'tax_name3' => $this->getString($data, 'expense.tax_name3'),
|
||||
'tax_rate3' => $this->getFloat($data, 'expense.tax_rate3'),
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -57,10 +57,10 @@ class InvoiceTransformer extends BaseTransformer
|
||||
'discount' => $this->getFloat($invoice_data, 'invoice.discount'),
|
||||
'po_number' => $this->getString($invoice_data, 'invoice.po_number'),
|
||||
'date' => isset($invoice_data['invoice.date'])
|
||||
? date('Y-m-d', strtotime($invoice_data['invoice.date']))
|
||||
? date('Y-m-d', strtotime(str_replace("/","-",$invoice_data['invoice.date'])))
|
||||
: now()->format('Y-m-d'),
|
||||
'due_date' => isset($invoice_data['invoice.due_date'])
|
||||
? date('Y-m-d', strtotime($invoice_data['invoice.due_date']))
|
||||
? date('Y-m-d', strtotime(str_replace("/","-",$invoice_data['invoice.due_date'])))
|
||||
: null,
|
||||
'terms' => $this->getString($invoice_data, 'invoice.terms'),
|
||||
'public_notes' => $this->getString(
|
||||
|
@ -57,10 +57,10 @@ class QuoteTransformer extends BaseTransformer
|
||||
'discount' => $this->getFloat($quote_data, 'quote.discount'),
|
||||
'po_number' => $this->getString($quote_data, 'quote.po_number'),
|
||||
'date' => isset($quote_data['quote.date'])
|
||||
? date('Y-m-d', strtotime($quote_data['quote.date']))
|
||||
? date('Y-m-d', strtotime(str_replace("/","-",$quote_data['quote.date'])))
|
||||
: now()->format('Y-m-d'),
|
||||
'due_date' => isset($quote_data['quote.due_date'])
|
||||
? date('Y-m-d', strtotime($quote_data['quote.due_date']))
|
||||
? date('Y-m-d', strtotime(str_replace("/","-",$quote_data['quote.due_date'])))
|
||||
: null,
|
||||
'terms' => $this->getString($quote_data, 'quote.terms'),
|
||||
'public_notes' => $this->getString(
|
||||
|
@ -86,7 +86,7 @@ class CreateAccount
|
||||
$sp794f3f->hosted_company_count = config('ninja.quotas.free.max_companies');
|
||||
$sp794f3f->account_sms_verified = true;
|
||||
|
||||
if(in_array($this->getDomain($this->request['email']), ['gmail.com', 'hotmail.com', 'outlook.com', 'yahoo.com'])){
|
||||
if(in_array($this->getDomain($this->request['email']), ['gmail.com', 'hotmail.com', 'outlook.com', 'yahoo.com', 'aol.com', 'mail.ru'])){
|
||||
$sp794f3f->account_sms_verified = false;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace App\Jobs\Cron;
|
||||
|
||||
use App\Jobs\RecurringInvoice\SendRecurring;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
@ -107,7 +108,7 @@ class RecurringInvoicesCron
|
||||
nlog("Trying to send {$recurring_invoice->number}");
|
||||
|
||||
if ($recurring_invoice->company->stop_on_unpaid_recurring) {
|
||||
if ($recurring_invoice->invoices()->whereIn('status_id', [2, 3])->where('is_deleted', 0)->where('balance', '>', 0)->exists()) {
|
||||
if (Invoice::where('recurring_id', $recurring_invoice->id)->whereIn('status_id', [2, 3])->where('is_deleted', 0)->where('balance', '>', 0)->exists()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ use App\Models\Activity;
|
||||
use App\Models\Company;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\PurchaseOrderInvitation;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Utils\HtmlEngine;
|
||||
@ -77,12 +78,12 @@ class EmailEntity implements ShouldQueue
|
||||
|
||||
$this->invitation = $invitation;
|
||||
|
||||
$this->settings = $invitation->contact->client->getMergedSettings();
|
||||
|
||||
$this->entity_string = $this->resolveEntityString();
|
||||
|
||||
$this->entity = $invitation->{$this->entity_string};
|
||||
|
||||
$this->settings = $invitation->contact->client->getMergedSettings();
|
||||
|
||||
$this->reminder_template = $reminder_template ?: $this->entity->calculateTemplate($this->entity_string);
|
||||
|
||||
$this->html_engine = new HtmlEngine($invitation);
|
||||
|
@ -37,7 +37,7 @@ class AdjustProductInventory implements ShouldQueue
|
||||
|
||||
public array $old_invoice;
|
||||
|
||||
public function __construct(Company $company, Invoice $invoice, ?array $old_invoice = [])
|
||||
public function __construct(Company $company, Invoice $invoice, $old_invoice = [])
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->invoice = $invoice;
|
||||
|
@ -51,7 +51,7 @@ class NinjaMailerJob implements ShouldQueue
|
||||
|
||||
public $tries = 3; //number of retries
|
||||
|
||||
public $backoff = 10; //seconds to wait until retry
|
||||
public $backoff = 30; //seconds to wait until retry
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
@ -123,7 +123,7 @@ class NinjaMailerJob implements ShouldQueue
|
||||
->send($this->nmo->mailable);
|
||||
|
||||
LightLogs::create(new EmailSuccess($this->nmo->company->company_key))
|
||||
->queue();
|
||||
->batch();
|
||||
|
||||
/* Count the amount of emails sent across all the users accounts */
|
||||
Cache::increment($this->company->account->key);
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Jobs\Ninja;
|
||||
|
||||
use App\DataMapper\Analytics\EmailCount;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
@ -20,6 +21,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
|
||||
class AdjustEmailQuota implements ShouldQueue
|
||||
{
|
||||
@ -58,8 +60,15 @@ class AdjustEmailQuota implements ShouldQueue
|
||||
{
|
||||
Account::query()->cursor()->each(function ($account) {
|
||||
nlog("resetting email quota for {$account->key}");
|
||||
|
||||
$email_count = Cache::get($account->key);
|
||||
|
||||
if($email_count > 0)
|
||||
LightLogs::create(new EmailCount($email_count, $account->key))->batch();
|
||||
|
||||
Cache::forget($account->key);
|
||||
Cache::forget("throttle_notified:{$account->key}");
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
||||
$this->request['MessageID']
|
||||
);
|
||||
|
||||
LightLogs::create($bounce)->queue();
|
||||
LightLogs::create($bounce)->batch();
|
||||
|
||||
SystemLogger::dispatch($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
|
||||
@ -263,7 +263,7 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
||||
$this->request['MessageID']
|
||||
);
|
||||
|
||||
LightLogs::create($spam)->queue();
|
||||
LightLogs::create($spam)->batch();
|
||||
|
||||
SystemLogger::dispatch($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
|
||||
|
@ -77,7 +77,12 @@ class PurchaseOrderEmail implements ShouldQueue
|
||||
/* Mark entity sent */
|
||||
$invitation->purchase_order->service()->markSent()->save();
|
||||
|
||||
$email_builder = (new PurchaseOrderEmailEngine($invitation, 'purchase_order', $this->template_data))->build();
|
||||
if(is_array($this->template_data) && array_key_exists('template', $this->template_data))
|
||||
$template = $this->template_data['template'];
|
||||
else
|
||||
$template = 'purchase_order';
|
||||
|
||||
$email_builder = (new PurchaseOrderEmailEngine($invitation, $template, $this->template_data))->build();
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new VendorTemplateEmail($email_builder, $invitation->contact, $invitation);
|
||||
@ -86,7 +91,7 @@ class PurchaseOrderEmail implements ShouldQueue
|
||||
$nmo->to_user = $invitation->contact;
|
||||
$nmo->entity_string = 'purchase_order';
|
||||
$nmo->invitation = $invitation;
|
||||
$nmo->reminder_template = 'purchase_order';
|
||||
$nmo->reminder_template = 'email_template_purchase_order';
|
||||
$nmo->entity = $invitation->purchase_order;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo)->delay(5);
|
||||
|
@ -73,8 +73,11 @@ class SendRecurring implements ShouldQueue
|
||||
$invoice->auto_bill_enabled = false;
|
||||
}
|
||||
|
||||
$invoice->date = now()->format('Y-m-d');
|
||||
$invoice->due_date = $this->recurring_invoice->calculateDueDate(now()->format('Y-m-d'));
|
||||
$invoice->date = date('Y-m-d');
|
||||
|
||||
nlog("Recurring Invoice Date Set on Invoice = {$invoice->date} - ". now()->format('Y-m-d'));
|
||||
|
||||
$invoice->due_date = $this->recurring_invoice->calculateDueDate(date('Y-m-d'));
|
||||
$invoice->recurring_id = $this->recurring_invoice->id;
|
||||
$invoice->saveQuietly();
|
||||
|
||||
@ -96,7 +99,7 @@ class SendRecurring implements ShouldQueue
|
||||
/* 09-01-2022 ensure we create the PDFs at this point in time! */
|
||||
$invoice->service()->touchPdf(true);
|
||||
|
||||
nlog('updating recurring invoice dates');
|
||||
//nlog('updating recurring invoice dates');
|
||||
/* Set next date here to prevent a recurring loop forming */
|
||||
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
||||
$this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient();
|
||||
@ -108,9 +111,9 @@ class SendRecurring implements ShouldQueue
|
||||
$this->recurring_invoice->setCompleted();
|
||||
}
|
||||
|
||||
// nlog('next send date = '.$this->recurring_invoice->next_send_date);
|
||||
//nlog('next send date = '.$this->recurring_invoice->next_send_date);
|
||||
// nlog('remaining cycles = '.$this->recurring_invoice->remaining_cycles);
|
||||
// nlog('last send date = '.$this->recurring_invoice->last_sent_date);
|
||||
//nlog('last send date = '.$this->recurring_invoice->last_sent_date);
|
||||
|
||||
$this->recurring_invoice->save();
|
||||
|
||||
|
@ -264,7 +264,7 @@ class Import implements ShouldQueue
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
Mail::to($this->user->email, $this->user->name())
|
||||
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
||||
->send(new MigrationCompleted($this->company->id, $this->company->db, implode("<br>",$check_data)));
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
nlog($e->getMessage());
|
||||
@ -715,6 +715,15 @@ class Import implements ShouldQueue
|
||||
|
||||
Client::reguard();
|
||||
|
||||
Client::with('contacts')->where('company_id', $this->company->id)->cursor()->each(function ($client){
|
||||
|
||||
$contact = $client->contacts->sortByDesc('is_primary')->first();
|
||||
$contact->is_primary = true;
|
||||
$contact->save();
|
||||
|
||||
});
|
||||
|
||||
|
||||
/*Improve memory handling by setting everything to null when we have finished*/
|
||||
$data = null;
|
||||
$contact_repository = null;
|
||||
|
@ -62,7 +62,8 @@ class ReminderJob implements ShouldQueue
|
||||
{
|
||||
nlog('Sending invoice reminders '.now()->format('Y-m-d h:i:s'));
|
||||
|
||||
Invoice::where('is_deleted', 0)
|
||||
Invoice::query()
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->whereNull('deleted_at')
|
||||
->where('balance', '>', 0)
|
||||
|
@ -329,6 +329,24 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByInappTransactionId($transaction_id) :bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
if (Account::on($db)->where('inapp_transaction_id', $transaction_id)->exists()) {
|
||||
self::setDb($db);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
self::setDB($current_db);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function findAndSetDbByContactKey($contact_key) :bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
58
app/Listeners/Subscription/AppStoreRenewSubscription.php
Normal file
58
app/Listeners/Subscription/AppStoreRenewSubscription.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Subscription;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Imdhemy\Purchases\Events\AppStore\DidRenew;
|
||||
|
||||
class AppStoreRenewSubscription implements ShouldQueue
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(DidRenew $event)
|
||||
{
|
||||
|
||||
$inapp_transaction_id = $event->getSubscriptionId(); //$subscription_id
|
||||
|
||||
MultiDB::findAndSetDbByInappTransactionId($inapp_transaction_id);
|
||||
|
||||
$account = Account::where('inapp_transaction_id', $inapp_transaction_id)->first();
|
||||
|
||||
if($account->plan_term == 'month')
|
||||
$account->plan_expires = now()->addMonth();
|
||||
elseif($account->plan_term == 'year')
|
||||
$account->plan_expires = now()->addYear();
|
||||
|
||||
$account->save();
|
||||
|
||||
// $server_notification = $event->getServerNotification();
|
||||
// $subscription = $event->getSubscription();
|
||||
// $subscription_identifier = $event->getSubscriptionIdentifier();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@ -13,19 +14,23 @@ class MigrationCompleted extends Mailable
|
||||
{
|
||||
// use Queueable, SerializesModels;
|
||||
|
||||
public $company;
|
||||
public $company_id;
|
||||
|
||||
public $db;
|
||||
|
||||
public $check_data;
|
||||
|
||||
public $company;
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Company $company, $check_data = '')
|
||||
public function __construct(int $company_id, string $db, $check_data = '')
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->company_id = $company_id;
|
||||
$this->check_data = $check_data;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,6 +40,10 @@ class MigrationCompleted extends Mailable
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
$this->company = Company::find($this->company_id);
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
@ -8,6 +8,7 @@ use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use LimitIterator;
|
||||
use SplFileObject;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class SupportMessageSent extends Mailable
|
||||
{
|
||||
@ -72,8 +73,8 @@ class SupportMessageSent extends Mailable
|
||||
|
||||
$plan_status = '';
|
||||
|
||||
if(Carbon::parse($account->plan_expires)->lt(now()))
|
||||
$plan_status = 'Plan Expired';
|
||||
if($account->plan_expires && Carbon::parse($account->plan_expires)->lt(now()))
|
||||
$plan_status = 'Plan Expired :: ';
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
$subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated}{$trial} :: {$plan} :: {$plan_status} ".date('M jS, g:ia');
|
||||
|
@ -91,7 +91,8 @@ class TemplateEmail extends Mailable
|
||||
if (strlen($settings->bcc_email) > 1) {
|
||||
if (Ninja::isHosted()) {
|
||||
$bccs = explode(',', str_replace(' ', '', $settings->bcc_email));
|
||||
$this->bcc(reset($bccs)); //remove whitespace if any has been inserted.
|
||||
$this->bcc(array_slice($bccs, 0, 2));
|
||||
//$this->bcc(reset($bccs)); //remove whitespace if any has been inserted.
|
||||
} else {
|
||||
$this->bcc(explode(',', str_replace(' ', '', $settings->bcc_email)));
|
||||
}//remove whitespace if any has been inserted.
|
||||
@ -116,11 +117,6 @@ class TemplateEmail extends Mailable
|
||||
'whitelabel' => $this->client->user->account->isPaid() ? true : false,
|
||||
'logo' => $this->company->present()->logo($settings),
|
||||
]);
|
||||
// ->withSymfonyMessage(function ($message) use ($company) {
|
||||
// $message->getHeaders()->addTextHeader('Tag', $company->company_key);
|
||||
// $message->invitation = $this->invitation;
|
||||
//});
|
||||
// ->tag($company->company_key);
|
||||
|
||||
/*In the hosted platform we need to slow things down a little for Storage to catch up.*/
|
||||
|
||||
|
@ -61,7 +61,7 @@ class VendorTemplateEmail extends Mailable
|
||||
}
|
||||
|
||||
if ($this->build_email->getTemplate() == 'custom') {
|
||||
$this->build_email->setBody(str_replace('$body', $this->build_email->getBody(), $this->client->getSetting('email_style_custom')));
|
||||
$this->build_email->setBody(str_replace('$body', $this->build_email->getBody(), $this->company->getSetting('email_style_custom')));
|
||||
}
|
||||
|
||||
$settings = $this->company->settings;
|
||||
|
@ -613,19 +613,19 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
{
|
||||
$defaults = [];
|
||||
|
||||
if (! (array_key_exists('terms', $data) && strlen($data['terms']) > 1)) {
|
||||
if (! (array_key_exists('terms', $data) && is_string($data['terms']) && strlen($data['terms']) > 1)) {
|
||||
$defaults['terms'] = $this->getSetting($entity_name.'_terms');
|
||||
} elseif (array_key_exists('terms', $data)) {
|
||||
$defaults['terms'] = $data['terms'];
|
||||
}
|
||||
|
||||
if (! (array_key_exists('footer', $data) && strlen($data['footer']) > 1)) {
|
||||
if (! (array_key_exists('footer', $data) && is_string($data['footer']) && strlen($data['footer']) > 1)) {
|
||||
$defaults['footer'] = $this->getSetting($entity_name.'_footer');
|
||||
} elseif (array_key_exists('footer', $data)) {
|
||||
$defaults['footer'] = $data['footer'];
|
||||
}
|
||||
|
||||
if (strlen($this->public_notes) >= 1) {
|
||||
if (is_string($this->public_notes) && strlen($this->public_notes) >= 1) {
|
||||
$defaults['public_notes'] = $this->public_notes;
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,7 @@ class Company extends BaseModel
|
||||
'stock_notification',
|
||||
'enabled_expense_tax_rates',
|
||||
'invoice_task_project',
|
||||
'report_include_deleted',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
@ -560,7 +560,7 @@ class RecurringInvoice extends BaseModel
|
||||
break;
|
||||
|
||||
case 'on_receipt':
|
||||
return Carbon::Parse($date)->copy();
|
||||
return Carbon::parse($date)->copy();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -64,7 +64,7 @@ class Vendor extends BaseModel
|
||||
protected $touches = [];
|
||||
|
||||
protected $with = [
|
||||
'company',
|
||||
'contacts.company',
|
||||
];
|
||||
|
||||
protected $presenter = VendorPresenter::class;
|
||||
@ -108,12 +108,13 @@ class Vendor extends BaseModel
|
||||
}
|
||||
|
||||
if (! $this->currency_id) {
|
||||
$this->currency_id = 1;
|
||||
return $this->company->currency();
|
||||
}
|
||||
|
||||
return $currencies->filter(function ($item) {
|
||||
return $item->id == $this->currency_id;
|
||||
})->first();
|
||||
|
||||
}
|
||||
|
||||
public function company()
|
||||
|
@ -45,10 +45,7 @@ class VendorContact extends Authenticatable implements HasLocalePreference
|
||||
'hashed_id',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
// 'vendor',
|
||||
// 'company'
|
||||
];
|
||||
protected $with = [];
|
||||
|
||||
protected $casts = [
|
||||
'updated_at' => 'timestamp',
|
||||
|
@ -70,6 +70,12 @@ class Webhook extends BaseModel
|
||||
|
||||
const EVENT_PROJECT_UPDATE = 26;
|
||||
|
||||
const EVENT_CREATE_CREDIT = 27;
|
||||
|
||||
const EVENT_UPDATE_CREDIT = 28;
|
||||
|
||||
const EVENT_DELETE_CREDIT = 29;
|
||||
|
||||
public static $valid_events = [
|
||||
self::EVENT_CREATE_CLIENT,
|
||||
self::EVENT_CREATE_INVOICE,
|
||||
@ -97,6 +103,9 @@ class Webhook extends BaseModel
|
||||
self::EVENT_REMIND_INVOICE,
|
||||
self::EVENT_PROJECT_CREATE,
|
||||
self::EVENT_PROJECT_UPDATE,
|
||||
self::EVENT_CREATE_CREDIT,
|
||||
self::EVENT_UPDATE_CREDIT,
|
||||
self::EVENT_DELETE_CREDIT,
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
|
86
app/Notifications/Ninja/DomainFailureNotification.php
Normal file
86
app/Notifications/Ninja/DomainFailureNotification.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Notifications\Ninja;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class DomainFailureNotification extends Notification
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
protected string $domain;
|
||||
|
||||
public function __construct(string $domain)
|
||||
{
|
||||
$this->domain = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['slack'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack($notifiable)
|
||||
{
|
||||
$content = "Domain Certificate failure:\n";
|
||||
$content .= "{$this->domain}\n";
|
||||
|
||||
return (new SlackMessage)
|
||||
->success()
|
||||
->from(ctrans('texts.notification_bot'))
|
||||
->image('https://app.invoiceninja.com/favicon.png')
|
||||
->content($content);
|
||||
}
|
||||
}
|
@ -27,6 +27,13 @@ class CreditObserver
|
||||
*/
|
||||
public function created(Credit $credit)
|
||||
{
|
||||
$subscriptions = Webhook::where('company_id', $credit->company->id)
|
||||
->where('event_id', Webhook::EVENT_CREATE_CREDIT)
|
||||
->exists();
|
||||
|
||||
if ($subscriptions) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_CREATE_CREDIT, $credit, $credit->company)->delay(now()->addSeconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,6 +44,13 @@ class CreditObserver
|
||||
*/
|
||||
public function updated(Credit $credit)
|
||||
{
|
||||
$subscriptions = Webhook::where('company_id', $credit->company->id)
|
||||
->where('event_id', Webhook::EVENT_UPDATE_CREDIT)
|
||||
->exists();
|
||||
|
||||
if ($subscriptions) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_CREDIT, $credit, $credit->company)->delay(now()->addSeconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,6 +61,13 @@ class CreditObserver
|
||||
*/
|
||||
public function deleted(Credit $credit)
|
||||
{
|
||||
$subscriptions = Webhook::where('company_id', $credit->company->id)
|
||||
->where('event_id', Webhook::EVENT_DELETE_CREDIT)
|
||||
->exists();
|
||||
|
||||
if ($subscriptions) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_DELETE_CREDIT, $credit, $credit->company)->delay(now()->addSeconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,7 @@ class CreditCard
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->braintree;
|
||||
$data['threeds_enable'] = $this->braintree->company_gateway->getConfigField('threeds') ? "true" : "false";
|
||||
|
||||
return render('gateways.braintree.credit_card.authorize', $data);
|
||||
}
|
||||
@ -54,11 +55,32 @@ class CreditCard
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
|
||||
private function threeDParameters(array $data)
|
||||
{
|
||||
return [
|
||||
'amount' => $data['amount_with_fee'],
|
||||
'email' => $this->braintree->client->present()->email(),
|
||||
'billingAddress' => [
|
||||
'givenName' => $this->braintree->client->present()->first_name() ?: $this->braintree->client->present()->name(),
|
||||
'surname' => $this->braintree->client->present()->last_name() ?: '',
|
||||
'phoneNumber' => $this->braintree->client->present()->phone(),
|
||||
'streetAddress' => $this->braintree->client->address1 ?: '',
|
||||
'extendedAddress' =>$this->braintree->client->address2 ?: '',
|
||||
'locality' => $this->braintree->client->city ?: '',
|
||||
'postalCode' => $this->braintree->client->postal_code ?: '',
|
||||
'countryCodeAlpha2' => $this->braintree->client->country ? $this->braintree->client->country->iso_3166_2 : 'US',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->braintree;
|
||||
$data['client_token'] = $this->braintree->gateway->clientToken()->generate();
|
||||
|
||||
$data['threeds'] = $this->threeDParameters($data);
|
||||
$data['threeds_enable'] = $this->braintree->company_gateway->getConfigField('threeds') ? "true" : "false";
|
||||
|
||||
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
|
||||
/** https://developer.paypal.com/braintree/docs/reference/request/client-token/generate#merchant_account_id */
|
||||
$data['client_token'] = $this->braintree->gateway->clientToken()->generate([
|
||||
@ -78,6 +100,8 @@ class CreditCard
|
||||
*/
|
||||
public function paymentResponse(PaymentResponseRequest $request)
|
||||
{
|
||||
// nlog($request->all());
|
||||
|
||||
$state = [
|
||||
'server_response' => json_decode($request->gateway_response),
|
||||
'payment_hash' => $request->payment_hash,
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Http\Requests\Gateways\Checkout3ds\Checkout3dsRequest;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
@ -33,7 +34,8 @@ use Checkout\CheckoutArgumentException;
|
||||
use Checkout\CheckoutAuthorizationException;
|
||||
use Checkout\CheckoutDefaultSdk;
|
||||
use Checkout\CheckoutFourSdk;
|
||||
use Checkout\Common\CustomerRequest;
|
||||
use Checkout\Customers\CustomerRequest;
|
||||
use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest;
|
||||
use Checkout\Environment;
|
||||
use Checkout\Library\Exceptions\CheckoutHttpException;
|
||||
use Checkout\Models\Payments\IdSource;
|
||||
@ -253,12 +255,13 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
];
|
||||
} catch (CheckoutApiException $e) {
|
||||
// API error
|
||||
$request_id = $e->request_id;
|
||||
$http_status_code = $e->http_status_code;
|
||||
$error_details = $e->error_details;
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
|
||||
} catch (CheckoutArgumentException $e) {
|
||||
// Bad arguments
|
||||
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
|
||||
return [
|
||||
'transaction_reference' => null,
|
||||
'transaction_response' => json_encode($e->getMessage()),
|
||||
@ -269,6 +272,8 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
} catch (CheckoutAuthorizationException $e) {
|
||||
// Bad Invalid authorization
|
||||
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
|
||||
return [
|
||||
'transaction_reference' => null,
|
||||
'transaction_response' => json_encode($e->getMessage()),
|
||||
@ -285,12 +290,28 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
$response = $this->gateway->getCustomersClient()->get($this->client->present()->email());
|
||||
|
||||
return $response;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$request = new CustomerRequest();
|
||||
|
||||
if ($this->is_four_api) {
|
||||
$request = new FourCustomerRequest();
|
||||
}
|
||||
else{
|
||||
$request = new CustomerRequest();
|
||||
}
|
||||
|
||||
$request->email = $this->client->present()->email();
|
||||
$request->name = $this->client->present()->name();
|
||||
$request->phone = $this->client->present()->phone();
|
||||
|
||||
return $request;
|
||||
try {
|
||||
$response = $this->gateway->getCustomersClient()->create($request);
|
||||
} catch (\Exception $e) {
|
||||
// API error
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,8 @@ class ACH
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->forte;
|
||||
|
||||
return render('gateways.forte.ach.authorize', $data);
|
||||
}
|
||||
|
||||
@ -81,7 +83,7 @@ class ACH
|
||||
$this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data);
|
||||
$this->forte->payment_hash->save();
|
||||
|
||||
$data['gateway'] = $this;
|
||||
$data['gateway'] = $this->forte;
|
||||
return render('gateways.forte.ach.pay', $data);
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
//finalize payments on invoices here.
|
||||
}
|
||||
|
||||
if ($event['action'] === 'failed') {
|
||||
if ($event['action'] === 'failed' && array_key_exists('payment', $event['links'])) {
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $event['links']['payment'])
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
|
@ -125,6 +125,20 @@ class ACH
|
||||
|
||||
$bank_account = Customer::retrieveSource($request->customer, $request->source, [], $this->stripe->stripe_connect_auth);
|
||||
|
||||
/* Catch externally validated bank accounts and mark them as verified */
|
||||
if(property_exists($bank_account, 'status') && $bank_account->status == 'verified'){
|
||||
|
||||
$meta = $token->meta;
|
||||
$meta->state = 'authorized';
|
||||
$token->meta = $meta;
|
||||
$token->save();
|
||||
|
||||
return redirect()
|
||||
->route('client.payment_methods.show', $token->hashed_id)
|
||||
->with('message', __('texts.payment_method_verified'));
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
$bank_account->verify(['amounts' => request()->transactions]);
|
||||
|
||||
@ -151,6 +165,18 @@ class ACH
|
||||
$data['payment_method_id'] = GatewayType::BANK_TRANSFER;
|
||||
$data['customer'] = $this->stripe->findOrCreateCustomer();
|
||||
$data['amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency());
|
||||
$amount = $data['total']['amount_with_fee'];
|
||||
|
||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->stripe->payment_hash->invoices(), 'invoice_id')))
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
if ($invoice) {
|
||||
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
} else {
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
}
|
||||
|
||||
|
||||
$intent = false;
|
||||
|
||||
@ -162,6 +188,11 @@ class ACH
|
||||
'setup_future_usage' => 'off_session',
|
||||
'customer' => $data['customer']->id,
|
||||
'payment_method_types' => ['us_bank_account'],
|
||||
'description' => $description,
|
||||
'metadata' => [
|
||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||
'gateway_type_id' => GatewayType::BANK_TRANSFER,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -17,11 +17,12 @@ use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Stripe\ACH;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
use App\PaymentDrivers\Stripe\ACH;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Stripe\Exception\ApiConnectionException;
|
||||
@ -137,8 +138,10 @@ class Charge
|
||||
|
||||
if ($cgt->gateway_type_id == GatewayType::SEPA) {
|
||||
$payment_method_type = PaymentType::SEPA;
|
||||
$status = Payment::STATUS_PENDING;
|
||||
} else {
|
||||
$payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
|
||||
$status = Payment::STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
$data = [
|
||||
@ -148,7 +151,7 @@ class Charge
|
||||
'amount' => $amount,
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data);
|
||||
$payment = $this->stripe->createPayment($data, $status);
|
||||
$payment->meta = $cgt->meta;
|
||||
$payment->save();
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace App\PaymentDrivers\Stripe\Jobs;
|
||||
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
@ -21,6 +22,7 @@ use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Stripe\UpdatePaymentMethods;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -53,7 +55,7 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
|
||||
$company = Company::where('company_key', $this->company_key)->first();
|
||||
@ -145,7 +147,18 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
|
||||
$this->updateCreditCardPayment($payment_hash, $client);
|
||||
}
|
||||
elseif(array_key_exists('payment_method_types', $this->stripe_request['object']) && optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('us_bank_account', $this->stripe_request['object']['payment_method_types']))
|
||||
{
|
||||
nlog("hash found");
|
||||
|
||||
$hash = $this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash'];
|
||||
|
||||
$payment_hash = PaymentHash::where('hash', $hash)->first();
|
||||
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
|
||||
$client = $invoice->client;
|
||||
|
||||
$this->updateAchPayment($payment_hash, $client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -161,6 +174,81 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
private function updateAchPayment($payment_hash, $client)
|
||||
{
|
||||
$company_gateway = CompanyGateway::find($this->company_gateway_id);
|
||||
$payment_method_type = optional($this->stripe_request['object']['charges']['data'][0]['metadata'])['gateway_type_id'];
|
||||
$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::ACH,
|
||||
'amount' => $payment_hash->data->amount_with_fee,
|
||||
'transaction_reference' => $this->stripe_request['object']['charges']['data'][0]['id'],
|
||||
'gateway_type_id' => GatewayType::BANK_TRANSFER,
|
||||
];
|
||||
|
||||
$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,
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
$customer = $driver->getCustomer($this->stripe_request['object']['charges']['data'][0]['customer']);
|
||||
$method = $driver->getStripePaymentMethod($this->stripe_request['object']['charges']['data'][0]['payment_method']);
|
||||
$payment_method = $this->stripe_request['object']['charges']['data'][0]['payment_method'];
|
||||
|
||||
$token_exists = ClientGatewayToken::where([
|
||||
'gateway_customer_reference' => $customer->id,
|
||||
'token' => $payment_method,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $client->company_id,
|
||||
])->exists();
|
||||
|
||||
/* Already exists return */
|
||||
if ($token_exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->brand = (string) \sprintf('%s (%s)', $method->us_bank_account['bank_name'], ctrans('texts.ach'));
|
||||
$payment_meta->last4 = (string) $method->us_bank_account['last4'];
|
||||
$payment_meta->type = GatewayType::BANK_TRANSFER;
|
||||
$payment_meta->state = 'verified';
|
||||
|
||||
$data = [
|
||||
'payment_meta' => $payment_meta,
|
||||
'token' => $payment_method,
|
||||
'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];
|
||||
}
|
||||
|
||||
$driver->storeGatewayToken($data, $additional_data);
|
||||
|
||||
}
|
||||
catch(\Exception $e){
|
||||
nlog("failed to import payment methods");
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function updateCreditCardPayment($payment_hash, $client)
|
||||
{
|
||||
$company_gateway = CompanyGateway::find($this->company_gateway_id);
|
||||
|
@ -58,12 +58,12 @@ class UpdateCustomer implements ShouldQueue
|
||||
|
||||
$company = Company::where('company_key', $this->company_key)->first();
|
||||
|
||||
if($company->id !== config('ninja.ninja_default_company_id'))
|
||||
return;
|
||||
|
||||
$company_gateway = CompanyGateway::find($this->company_gateway_id);
|
||||
$client = Client::withTrashed()->find($this->client_id);
|
||||
|
||||
if(!$company_gateway->update_details)
|
||||
return;
|
||||
|
||||
$stripe = $company_gateway->driver($client)->init();
|
||||
|
||||
$customer = $stripe->findOrCreateCustomer();
|
||||
|
@ -39,6 +39,7 @@ class SEPA
|
||||
$data['client'] = $this->stripe->client;
|
||||
$data['country'] = $this->stripe->client->country->iso_3166_2;
|
||||
$data['currency'] = $this->stripe->client->currency();
|
||||
$data['payment_hash'] = 'x';
|
||||
|
||||
return render('gateways.stripe.sepa.authorize', $data);
|
||||
}
|
||||
|
@ -70,11 +70,11 @@ class UpdatePaymentMethods
|
||||
$this->importBankAccounts($customer, $client);
|
||||
}
|
||||
|
||||
private function importBankAccounts($customer, $client)
|
||||
public function importBankAccounts($customer, $client)
|
||||
{
|
||||
$sources = $customer->sources;
|
||||
|
||||
if(!property_exists($sources, 'data'))
|
||||
if(!$customer || !property_exists($sources, 'data'))
|
||||
return;
|
||||
|
||||
foreach ($sources->data as $method) {
|
||||
|
@ -511,6 +511,36 @@ class StripePaymentDriver extends BaseDriver
|
||||
}
|
||||
}
|
||||
|
||||
public function updateCustomer()
|
||||
{
|
||||
if($this->client)
|
||||
{
|
||||
|
||||
$customer = $this->findOrCreateCustomer();
|
||||
//Else create a new record
|
||||
$data['name'] = $this->client->present()->name();
|
||||
$data['phone'] = substr($this->client->present()->phone(), 0, 20);
|
||||
|
||||
$data['address']['line1'] = $this->client->address1;
|
||||
$data['address']['line2'] = $this->client->address2;
|
||||
$data['address']['city'] = $this->client->city;
|
||||
$data['address']['postal_code'] = $this->client->postal_code;
|
||||
$data['address']['state'] = $this->client->state;
|
||||
$data['address']['country'] = $this->client->country ? $this->client->country->iso_3166_2 : '';
|
||||
|
||||
$data['shipping']['name'] = $this->client->present()->name();
|
||||
$data['shipping']['address']['line1'] = $this->client->shipping_address1;
|
||||
$data['shipping']['address']['line2'] = $this->client->shipping_address2;
|
||||
$data['shipping']['address']['city'] = $this->client->shipping_city;
|
||||
$data['shipping']['address']['postal_code'] = $this->client->shipping_postal_code;
|
||||
$data['shipping']['address']['state'] = $this->client->shipping_state;
|
||||
$data['shipping']['address']['country'] = $this->client->shipping_country ? $this->client->shipping_country->iso_3166_2 : '';
|
||||
|
||||
\Stripe\Customer::update($customer->id, $data, $this->stripe_connect_auth);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
$this->init();
|
||||
|
@ -41,6 +41,7 @@ class ACH
|
||||
public function authorizeView($data)
|
||||
{
|
||||
$data['gateway'] = $this->wepay_payment_driver;
|
||||
$data['country_code'] = $this->wepay_payment_driver->client ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
|
||||
|
||||
return render('gateways.wepay.authorize.bank_transfer', $data);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ class CreditCard
|
||||
public function authorizeView($data)
|
||||
{
|
||||
$data['gateway'] = $this->wepay_payment_driver;
|
||||
$data['country_code'] = $this->wepay_payment_driver->client ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
|
||||
|
||||
return render('gateways.wepay.authorize.authorize', $data);
|
||||
}
|
||||
@ -101,6 +102,7 @@ class CreditCard
|
||||
{
|
||||
$data['gateway'] = $this->wepay_payment_driver;
|
||||
$data['description'] = ctrans('texts.invoices').': '.collect($data['invoices'])->pluck('invoice_number');
|
||||
$data['country_code'] = $this->wepay_payment_driver->client ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
|
||||
|
||||
return render('gateways.wepay.credit_card.pay', $data);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class Setup
|
||||
{
|
||||
/*
|
||||
'user_id',
|
||||
'company',
|
||||
'user_company',
|
||||
*/
|
||||
|
||||
return render('gateways.wepay.signup.index', $data);
|
||||
|
@ -47,7 +47,7 @@ class EntityPolicy
|
||||
public function edit(User $user, $entity) : bool
|
||||
{
|
||||
return ($user->isAdmin() && $entity->company_id == $user->companyId())
|
||||
|| ($user->hasPermission('edit_'.strtolower(class_basename($entity))) && $entity->company_id == $user->companyId())
|
||||
|| ($user->hasPermission('edit_'.strtolower(\Illuminate\Support\Str::snake(class_basename($entity)))) && $entity->company_id == $user->companyId())
|
||||
|| ($user->hasPermission('edit_all') && $entity->company_id == $user->companyId())
|
||||
|| $user->owns($entity)
|
||||
|| $user->assigned($entity);
|
||||
@ -64,7 +64,7 @@ class EntityPolicy
|
||||
public function view(User $user, $entity) : bool
|
||||
{
|
||||
return ($user->isAdmin() && $entity->company_id == $user->companyId())
|
||||
|| ($user->hasPermission('view_'.strtolower(class_basename($entity))) && $entity->company_id == $user->companyId())
|
||||
|| ($user->hasPermission('view_'.strtolower(\Illuminate\Support\Str::snake(class_basename($entity)))) && $entity->company_id == $user->companyId())
|
||||
|| ($user->hasPermission('view_all') && $entity->company_id == $user->companyId())
|
||||
|| $user->owns($entity)
|
||||
|| $user->assigned($entity);
|
||||
|
@ -304,7 +304,9 @@ class BaseRepository
|
||||
|
||||
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
|
||||
|
||||
//10-07-2022
|
||||
//14-09-2022 log when we make changes to the invoice balance.
|
||||
nlog("Adjustment - {$model->number} - " .$state['finished_amount']. " - " . $state['starting_amount']);
|
||||
|
||||
$model->service()->updateStatus()->save();
|
||||
$model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save();
|
||||
$model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount']), "Update adjustment for invoice {$model->number}");
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
@ -16,6 +17,7 @@ use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use App\Models\Expense;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
/**
|
||||
* ExpenseRepository.
|
||||
@ -24,6 +26,8 @@ class ExpenseRepository extends BaseRepository
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
private $completed = true;
|
||||
|
||||
/**
|
||||
* Saves the expense and its contacts.
|
||||
*
|
||||
@ -32,15 +36,17 @@ class ExpenseRepository extends BaseRepository
|
||||
*
|
||||
* @return \App\Models\Expense|null expense Object
|
||||
*/
|
||||
public function save(array $data, Expense $expense) : ?Expense
|
||||
public function save(array $data, Expense $expense): ?Expense
|
||||
{
|
||||
$expense->fill($data);
|
||||
|
||||
if (! $expense->id) {
|
||||
if (!$expense->id) {
|
||||
$expense = $this->processExchangeRates($data, $expense);
|
||||
}
|
||||
|
||||
$expense->number = empty($expense->number) ? $this->getNextExpenseNumber($expense) : $expense->number;
|
||||
if (empty($expense->number))
|
||||
$expense = $this->findAndSaveNumber($expense);
|
||||
|
||||
$expense->save();
|
||||
|
||||
if (array_key_exists('documents', $data)) {
|
||||
@ -54,6 +60,7 @@ class ExpenseRepository extends BaseRepository
|
||||
* Store expenses in bulk.
|
||||
*
|
||||
* @param array $expense
|
||||
*
|
||||
* @return \App\Models\Expense|null
|
||||
*/
|
||||
public function create($expense): ?Expense
|
||||
@ -64,7 +71,7 @@ class ExpenseRepository extends BaseRepository
|
||||
);
|
||||
}
|
||||
|
||||
public function processExchangeRates($data, $expense)
|
||||
public function processExchangeRates($data, $expense): Expense
|
||||
{
|
||||
if (array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']) && $data['exchange_rate'] != 1) {
|
||||
return $expense;
|
||||
@ -83,4 +90,35 @@ class ExpenseRepository extends BaseRepository
|
||||
|
||||
return $expense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle race conditions when creating expense numbers
|
||||
*
|
||||
* @param Expense $expense
|
||||
* @return \App\Models\Expense
|
||||
*/
|
||||
private function findAndSaveNumber($expense): Expense
|
||||
{
|
||||
|
||||
$x = 1;
|
||||
|
||||
do {
|
||||
|
||||
try {
|
||||
|
||||
$expense->number = $this->getNextExpenseNumber($expense);
|
||||
$expense->saveQuietly();
|
||||
|
||||
$this->completed = false;
|
||||
} catch (QueryException $e) {
|
||||
|
||||
$x++;
|
||||
|
||||
if ($x > 50)
|
||||
$this->completed = false;
|
||||
}
|
||||
} while ($this->completed);
|
||||
|
||||
return $expense;
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,6 @@ class InvoiceRepository extends BaseRepository
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
// $invoice->service()->markDeleted()->handleCancellation()->save();
|
||||
$invoice = $invoice->service()->markDeleted()->save();
|
||||
|
||||
parent::delete($invoice);
|
||||
|
@ -144,7 +144,7 @@ class PaymentRepository extends BaseRepository {
|
||||
|
||||
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
|
||||
|
||||
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
||||
$invoices = Invoice::withTrashed()->whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
||||
|
||||
$payment->invoices()->saveMany($invoices);
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace App\Repositories;
|
||||
use App\Factory\TaskFactory;
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
/**
|
||||
* TaskRepository.
|
||||
@ -24,6 +25,8 @@ class TaskRepository extends BaseRepository
|
||||
|
||||
public $new_task = true;
|
||||
|
||||
private $completed = true;
|
||||
|
||||
/**
|
||||
* Saves the task and its contacts.
|
||||
*
|
||||
@ -45,7 +48,7 @@ class TaskRepository extends BaseRepository
|
||||
$this->setDefaultStatus($task);
|
||||
}
|
||||
|
||||
$task->number = empty($task->number) || ! array_key_exists('number', $data) ? $this->getNextTaskNumber($task) : $data['number'];
|
||||
$task->number = empty($task->number) || ! array_key_exists('number', $data) ? $this->trySaving($task) : $data['number'];
|
||||
|
||||
if (isset($data['description'])) {
|
||||
$task->description = trim($data['description']);
|
||||
@ -244,4 +247,34 @@ class TaskRepository extends BaseRepository
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
|
||||
private function trySaving(Task $task)
|
||||
{
|
||||
|
||||
$x=1;
|
||||
|
||||
do{
|
||||
|
||||
try{
|
||||
|
||||
$task->number = $this->getNextTaskNumber($task);
|
||||
$task->saveQuietly();
|
||||
$this->completed = false;
|
||||
|
||||
}
|
||||
catch(QueryException $e){
|
||||
|
||||
$x++;
|
||||
|
||||
if($x>50)
|
||||
$this->completed = false;
|
||||
}
|
||||
|
||||
}
|
||||
while($this->completed);
|
||||
|
||||
return $task->number;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class ClientService
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($amount) {
|
||||
|
||||
$this->client = Client::where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client->balance += $amount;
|
||||
$this->client->save();
|
||||
|
||||
@ -49,7 +49,7 @@ class ClientService
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($balance, $paid_to_date) {
|
||||
|
||||
$this->client = Client::where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client->balance += $balance;
|
||||
$this->client->paid_to_date += $paid_to_date;
|
||||
$this->client->save();
|
||||
@ -65,7 +65,7 @@ class ClientService
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($amount) {
|
||||
|
||||
$this->client = Client::where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client->paid_to_date += $amount;
|
||||
$this->client->save();
|
||||
|
||||
@ -83,7 +83,7 @@ class ClientService
|
||||
|
||||
public function getCreditBalance() :float
|
||||
{
|
||||
$credits = Credit::where('client_id', $this->client->id)
|
||||
$credits = Credit::withTrashed()->where('client_id', $this->client->id)
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->where(function ($query) {
|
||||
|
@ -70,7 +70,6 @@ class ApplyNumber extends AbstractService
|
||||
$this->invoice->saveQuietly();
|
||||
|
||||
$this->completed = false;
|
||||
|
||||
|
||||
}
|
||||
catch(QueryException $e){
|
||||
@ -84,5 +83,8 @@ class ApplyNumber extends AbstractService
|
||||
}
|
||||
while($this->completed);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class MarkPaid extends AbstractService
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () {
|
||||
|
||||
$this->invoice = Invoice::where('id', $this->invoice->id)->lockForUpdate()->first();
|
||||
$this->invoice = Invoice::withTrashed()->where('id', $this->invoice->id)->lockForUpdate()->first();
|
||||
|
||||
$this->payable_balance = $this->invoice->balance;
|
||||
|
||||
|
@ -44,6 +44,10 @@ class TriggeredActions extends AbstractService
|
||||
$this->invoice = $this->invoice->service()->markPaid()->save();
|
||||
}
|
||||
|
||||
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
|
||||
$this->invoice = $this->invoice->service()->markSent()->save();
|
||||
}
|
||||
|
||||
if ($this->request->has('amount_paid') && is_numeric($this->request->input('amount_paid'))) {
|
||||
$this->invoice = $this->invoice->service()->applyPaymentAmount($this->request->input('amount_paid'))->save();
|
||||
}
|
||||
@ -53,10 +57,6 @@ class TriggeredActions extends AbstractService
|
||||
$this->sendEmail();
|
||||
}
|
||||
|
||||
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
|
||||
$this->invoice = $this->invoice->service()->markSent()->save();
|
||||
}
|
||||
|
||||
if ($this->request->has('cancel') && $this->request->input('cancel') == 'true') {
|
||||
$this->invoice = $this->invoice->service()->handleCancellation()->save();
|
||||
}
|
||||
|
@ -33,17 +33,29 @@ class DeletePayment
|
||||
|
||||
public function run()
|
||||
{
|
||||
if ($this->payment->is_deleted) {
|
||||
return $this->payment;
|
||||
}
|
||||
|
||||
return $this->setStatus(Payment::STATUS_CANCELLED) //sets status of payment
|
||||
->updateCreditables() //return the credits first
|
||||
->adjustInvoices()
|
||||
->updateClient()
|
||||
->deletePaymentables()
|
||||
->cleanupPayment()
|
||||
->save();
|
||||
\DB::connection(config('database.default'))->transaction(function () {
|
||||
|
||||
|
||||
if ($this->payment->is_deleted) {
|
||||
return $this->payment;
|
||||
}
|
||||
|
||||
$this->payment = Payment::withTrashed()->where('id', $this->payment->id)->lockForUpdate()->first();
|
||||
|
||||
$this->setStatus(Payment::STATUS_CANCELLED) //sets status of payment
|
||||
->updateCreditables() //return the credits first
|
||||
->adjustInvoices()
|
||||
->updateClient()
|
||||
->deletePaymentables()
|
||||
->cleanupPayment()
|
||||
->save();
|
||||
|
||||
|
||||
}, 2);
|
||||
|
||||
return $this->payment;
|
||||
|
||||
}
|
||||
|
||||
private function cleanupPayment()
|
||||
|
@ -109,6 +109,8 @@ class RecurringService
|
||||
|
||||
if ($request->has('send_now') && $request->input('send_now') == 'true' && $this->recurring_entity->invoices()->count() == 0) {
|
||||
$this->sendNow();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if(isset($this->recurring_entity->client))
|
||||
@ -125,10 +127,12 @@ class RecurringService
|
||||
|
||||
if($this->recurring_entity instanceof RecurringInvoice && $this->recurring_entity->status_id == RecurringInvoice::STATUS_DRAFT){
|
||||
$this->start()->save();
|
||||
SendRecurring::dispatch($this->recurring_entity, $this->recurring_entity->company->db);
|
||||
SendRecurring::dispatchSync($this->recurring_entity, $this->recurring_entity->company->db);
|
||||
}
|
||||
|
||||
return $this->recurring_entity;
|
||||
$this->recurring_entity = $this->recurring_entity->fresh();
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@ class AccountTransformer extends EntityTransformer
|
||||
{
|
||||
return [
|
||||
'id' => (string) $this->encodePrimaryKey($account->id),
|
||||
'key' => (string) $this->account->key,
|
||||
'default_url' => config('ninja.app_url'),
|
||||
'plan' => $account->getPlan(),
|
||||
'plan_term' => (string) $account->plan_terms,
|
||||
|
@ -185,6 +185,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'enable_applying_payments' => (bool) $company->enable_applying_payments,
|
||||
'enabled_expense_tax_rates' => (int) $company->enabled_expense_tax_rates,
|
||||
'invoice_task_project' => (bool) $company->invoice_task_project,
|
||||
'report_include_deleted' => (bool) $company->report_include_deleted,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,22 @@ class Helpers
|
||||
return '';
|
||||
}
|
||||
|
||||
// 04-10-2022 Return Early if no reserved keywords are present, this is a very expenseive process
|
||||
$string_hit = false;
|
||||
|
||||
foreach ( [':MONTH',':YEAR',':QUARTER',':WEEK'] as $string )
|
||||
{
|
||||
|
||||
if(stripos($value, $string) !== FALSE) {
|
||||
$string_hit = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!$string_hit)
|
||||
return $value;
|
||||
// 04-10-2022 Return Early if no reserved keywords are present, this is a very expenseive process
|
||||
|
||||
Carbon::setLocale($entity->locale());
|
||||
|
||||
$replacements = [
|
||||
|
@ -168,7 +168,7 @@ class HtmlEngine
|
||||
$data['$invoice.vendor'] = ['value' => $this->entity->vendor->present()->name(), 'label' => ctrans('texts.vendor_name')];
|
||||
}
|
||||
|
||||
if(strlen($this->company->getSetting('qr_iban')) > 5 && strlen($this->company->getSetting('besr_id')) > 1)
|
||||
if(strlen($this->company->getSetting('qr_iban')) > 5)
|
||||
{
|
||||
try{
|
||||
$data['$swiss_qr'] = ['value' => (new SwissQrGenerator($this->entity, $this->company))->run(), 'label' => ''];
|
||||
@ -522,6 +522,9 @@ class HtmlEngine
|
||||
$data['$font_name'] = ['value' => Helpers::resolveFont($this->settings->primary_font)['name'], 'label' => ''];
|
||||
$data['$font_url'] = ['value' => Helpers::resolveFont($this->settings->primary_font)['url'], 'label' => ''];
|
||||
|
||||
$data['$secondary_font_name'] = ['value' => Helpers::resolveFont($this->settings->secondary_font)['name'], 'label' => ''];
|
||||
$data['$secondary_font_url'] = ['value' => Helpers::resolveFont($this->settings->secondary_font)['url'], 'label' => ''];
|
||||
|
||||
$data['$invoiceninja.whitelabel'] = ['value' => 'https://raw.githubusercontent.com/invoiceninja/invoiceninja/v5-develop/public/images/new_logo.png', 'label' => ''];
|
||||
|
||||
$data['$primary_color'] = ['value' => $this->settings->primary_color, 'label' => ''];
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user