mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
commit
03d8864652
@ -1 +1 @@
|
||||
5.5.49
|
||||
5.5.50
|
@ -119,7 +119,8 @@ class CheckData extends Command
|
||||
$this->checkDuplicateRecurringInvoices();
|
||||
$this->checkOauthSanity();
|
||||
$this->checkVendorSettings();
|
||||
|
||||
$this->checkClientSettings();
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$this->checkAccountStatuses();
|
||||
$this->checkNinjaPortalUrls();
|
||||
@ -952,24 +953,24 @@ class CheckData extends Command
|
||||
|
||||
if ($this->option('fix') == 'true') {
|
||||
|
||||
Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){
|
||||
// Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){
|
||||
|
||||
if(is_array($client->settings) && count($client->settings) == 0)
|
||||
{
|
||||
$settings = ClientSettings::defaults();
|
||||
$settings->currency_id = $client->company->settings->currency_id;
|
||||
}
|
||||
else {
|
||||
$settings = $client->settings;
|
||||
$settings->currency_id = $client->company->settings->currency_id;
|
||||
}
|
||||
// if(is_array($client->settings) && count($client->settings) == 0)
|
||||
// {
|
||||
// $settings = ClientSettings::defaults();
|
||||
// $settings->currency_id = $client->company->settings->currency_id;
|
||||
// }
|
||||
// else {
|
||||
// $settings = $client->settings;
|
||||
// $settings->currency_id = $client->company->settings->currency_id;
|
||||
// }
|
||||
|
||||
$client->settings = $settings;
|
||||
$client->save();
|
||||
// $client->settings = $settings;
|
||||
// $client->save();
|
||||
|
||||
$this->logMessage("Fixing currency for # {$client->id}");
|
||||
// $this->logMessage("Fixing currency for # {$client->id}");
|
||||
|
||||
});
|
||||
// });
|
||||
|
||||
|
||||
Client::query()->whereNull('country_id')->cursor()->each(function ($client){
|
||||
|
@ -307,7 +307,7 @@ class CreateSingleAccount extends Command
|
||||
$webhook_config = [
|
||||
'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan',
|
||||
'post_purchase_rest_method' => 'POST',
|
||||
'post_purchase_headers' => [],
|
||||
'post_purchase_headers' => [config('ninja.ninja_hosted_header') => config('ninja.ninja_hosted_secret')],
|
||||
];
|
||||
|
||||
$sub = SubscriptionFactory::create($company->id, $user->id);
|
||||
|
@ -23,6 +23,7 @@ use App\Jobs\Ninja\QueueSize;
|
||||
use App\Jobs\Ninja\SystemMaintenance;
|
||||
use App\Jobs\Ninja\TaskScheduler;
|
||||
use App\Jobs\Quote\QuoteCheckExpired;
|
||||
use App\Jobs\Subscription\CleanStaleInvoiceOrder;
|
||||
use App\Jobs\Util\DiskCleanup;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SchedulerCheck;
|
||||
@ -68,6 +69,9 @@ class Kernel extends ConsoleKernel
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new CleanStaleInvoiceOrder)->hourly()->withoutOverlapping()->name('stale-invoice-job')->onOneServer();
|
||||
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
|
||||
|
||||
|
@ -93,6 +93,10 @@ class ClientRegistrationFields
|
||||
'key' => 'vat_number',
|
||||
'required' => false,
|
||||
],
|
||||
[
|
||||
'key' => 'currency_id',
|
||||
'required' => false,
|
||||
],
|
||||
];
|
||||
|
||||
return $data;
|
||||
|
@ -77,28 +77,45 @@ class BankTransactionFilters extends QueryFilters
|
||||
|
||||
$status_parameters = explode(',', $value);
|
||||
|
||||
$status_array = [];
|
||||
|
||||
$debit_or_withdrawal_array = [];
|
||||
|
||||
if (in_array('all', $status_parameters)) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
if (in_array('unmatched', $status_parameters)) {
|
||||
$this->builder->where('status_id', BankTransaction::STATUS_UNMATCHED);
|
||||
$status_array[] = BankTransaction::STATUS_UNMATCHED;
|
||||
// $this->builder->orWhere('status_id', BankTransaction::STATUS_UNMATCHED);
|
||||
}
|
||||
|
||||
if (in_array('matched', $status_parameters)) {
|
||||
$this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
|
||||
$status_array[] = BankTransaction::STATUS_MATCHED;
|
||||
// $this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
|
||||
}
|
||||
|
||||
if (in_array('converted', $status_parameters)) {
|
||||
$this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
|
||||
$status_array[] = BankTransaction::STATUS_CONVERTED;
|
||||
// $this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
|
||||
}
|
||||
|
||||
if (in_array('deposits', $status_parameters)) {
|
||||
$this->builder->where('base_type', 'CREDIT');
|
||||
$debit_or_withdrawal_array[] = 'CREDIT';
|
||||
// $this->builder->where('base_type', 'CREDIT');
|
||||
}
|
||||
|
||||
if (in_array('withdrawals', $status_parameters)) {
|
||||
$this->builder->where('base_type', 'DEBIT');
|
||||
$debit_or_withdrawal_array[] = 'DEBIT';
|
||||
// $this->builder->where('base_type', 'DEBIT');
|
||||
}
|
||||
|
||||
if(count($status_array) >=1) {
|
||||
$this->builder->whereIn('status_id', $status_array);
|
||||
}
|
||||
|
||||
if(count($debit_or_withdrawal_array) >=1) {
|
||||
$this->builder->orWhereIn('base_type', $debit_or_withdrawal_array);
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
|
@ -238,7 +238,6 @@ class ClientFilters extends QueryFilters
|
||||
*/
|
||||
public function entityFilter()
|
||||
{
|
||||
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
|
||||
return $this->builder->company();
|
||||
}
|
||||
}
|
||||
|
@ -47,20 +47,27 @@ class InvoiceFilters extends QueryFilters
|
||||
|
||||
$status_parameters = explode(',', $value);
|
||||
|
||||
$invoice_filters = [];
|
||||
|
||||
if (in_array('all', $status_parameters)) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
if (in_array('paid', $status_parameters)) {
|
||||
$this->builder->where('status_id', Invoice::STATUS_PAID);
|
||||
$invoice_filters[] = Invoice::STATUS_PAID;
|
||||
}
|
||||
|
||||
if (in_array('unpaid', $status_parameters)) {
|
||||
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
|
||||
$invoice_filters[] = Invoice::STATUS_SENT;
|
||||
$invoice_filters[] = Invoice::STATUS_PARTIAL;
|
||||
}
|
||||
|
||||
if(count($invoice_filters) >0){
|
||||
$this->builder->whereIn('status_id', $invoice_filters);
|
||||
}
|
||||
|
||||
if (in_array('overdue', $status_parameters)) {
|
||||
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
$this->builder->orWhereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('due_date', '<', Carbon::now())
|
||||
->orWhere('partial_due_date', '<', Carbon::now());
|
||||
}
|
||||
|
@ -42,20 +42,26 @@ class PurchaseOrderFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$po_status = [];
|
||||
|
||||
if (in_array('draft', $status_parameters)) {
|
||||
$this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT);
|
||||
$po_status[] = PurchaseOrder::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
if (in_array('sent', $status_parameters)) {
|
||||
$this->builder->where('status_id', PurchaseOrder::STATUS_SENT);
|
||||
$po_status[] = PurchaseOrder::STATUS_SENT;
|
||||
}
|
||||
|
||||
if (in_array('accepted', $status_parameters)) {
|
||||
$this->builder->where('status_id', PurchaseOrder::STATUS_ACCEPTED);
|
||||
$po_status[] = PurchaseOrder::STATUS_ACCEPTED;
|
||||
}
|
||||
|
||||
if (in_array('cancelled', $status_parameters)) {
|
||||
$this->builder->where('status_id', PurchaseOrder::STATUS_CANCELLED);
|
||||
$po_status[] = PurchaseOrder::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
if(count($status_parameters) >=1) {
|
||||
$this->builder->whereIn('status_id', $status_parameters);
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
|
@ -15,6 +15,7 @@ namespace App\Filters;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* Class QueryFilters.
|
||||
@ -173,22 +174,30 @@ abstract class QueryFilters
|
||||
}
|
||||
}
|
||||
|
||||
public function created_at($value)
|
||||
public function created_at($value = '')
|
||||
{
|
||||
$created_at = $value ? (int) $value : 0;
|
||||
|
||||
if($value == '')
|
||||
return $this->builder;
|
||||
|
||||
$created_at = date('Y-m-d H:i:s', $value);
|
||||
try{
|
||||
|
||||
if(is_string($created_at)){
|
||||
if(is_numeric($value)){
|
||||
$created_at = Carbon::createFromTimestamp((int)$value);
|
||||
}
|
||||
else{
|
||||
$created_at = Carbon::parse($value);
|
||||
}
|
||||
|
||||
$created_at = strtotime(str_replace("/","-",$created_at));
|
||||
|
||||
if(!$created_at)
|
||||
return $this->builder;
|
||||
return $this->builder->where('created_at', '>=', $created_at);
|
||||
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
|
||||
return $this->builder->where('created_at', '>=', $created_at);
|
||||
return $this->builder;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function is_deleted($value)
|
||||
|
@ -66,26 +66,32 @@ class QuoteFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$quote_filters = [];
|
||||
|
||||
if (in_array('draft', $status_parameters)) {
|
||||
$this->builder->where('status_id', Quote::STATUS_DRAFT);
|
||||
$quote_filters[] = Quote::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
if (in_array('sent', $status_parameters)) {
|
||||
$this->builder->where('status_id', Quote::STATUS_SENT);
|
||||
$quote_filters[] = Quote::STATUS_SENT;
|
||||
}
|
||||
|
||||
if (in_array('approved', $status_parameters)) {
|
||||
$this->builder->where('status_id', Quote::STATUS_APPROVED);
|
||||
$quote_filters[] = Quote::STATUS_APPROVED;
|
||||
}
|
||||
|
||||
if(count($quote_filters) >=1){
|
||||
$this->builder->whereIn('status_id', $quote_filters);
|
||||
}
|
||||
|
||||
if (in_array('expired', $status_parameters)) {
|
||||
$this->builder->where('status_id', Quote::STATUS_SENT)
|
||||
->where('due_date', '>=', now()->toDateString());
|
||||
$this->builder->orWhere('status_id', Quote::STATUS_SENT)
|
||||
->where('due_date', '<=', now()->toDateString());
|
||||
}
|
||||
|
||||
if (in_array('upcoming', $status_parameters)) {
|
||||
$this->builder->where('status_id', Quote::STATUS_SENT)
|
||||
->where('due_date', '<=', now()->toDateString())
|
||||
$this->builder->orWhere('status_id', Quote::STATUS_SENT)
|
||||
->where('due_date', '>=', now()->toDateString())
|
||||
->orderBy('due_date', 'DESC');
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ class ContactRegisterController extends Controller
|
||||
|
||||
public function register(RegisterRequest $request)
|
||||
{
|
||||
|
||||
$request->merge(['company' => $request->company()]);
|
||||
|
||||
$client = $this->getClient($request->all());
|
||||
@ -58,7 +59,7 @@ class ContactRegisterController extends Controller
|
||||
|
||||
Auth::guard('contact')->loginUsingId($client_contact->id, true);
|
||||
|
||||
return redirect()->route('client.dashboard');
|
||||
return redirect()->intended(route('client.dashboard'));
|
||||
}
|
||||
|
||||
private function getClient(array $data)
|
||||
@ -66,7 +67,15 @@ class ContactRegisterController extends Controller
|
||||
$client = ClientFactory::create($data['company']->id, $data['company']->owner()->id);
|
||||
|
||||
$client->fill($data);
|
||||
|
||||
$client->save();
|
||||
|
||||
if(isset($data['currency_id'])) {
|
||||
$settings = $client->settings;
|
||||
$settings->currency_id = isset($data['currency_id']) ? $data['currency_id'] : $data['company']->settings->currency_id;
|
||||
$client->settings = $settings;
|
||||
}
|
||||
|
||||
$client->number = $this->getNextClientNumber($client);
|
||||
$client->save();
|
||||
|
||||
|
@ -52,7 +52,7 @@ class InvoiceController extends Controller
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function show(ShowInvoiceRequest $request, Invoice $invoice)
|
||||
public function show(ShowInvoiceRequest $request, Invoice $invoice, ?string $hash = null)
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
@ -69,6 +69,7 @@ class InvoiceController extends Controller
|
||||
'invoice' => $invoice,
|
||||
'invitation' => $invitation ?: $invoice->invitations->first(),
|
||||
'key' => $invitation ? $invitation->key : false,
|
||||
'hash' => $hash,
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
|
@ -148,8 +148,17 @@ class PaymentController extends Controller
|
||||
|
||||
$payment = $payment->service()->applyCredits($payment_hash)->save();
|
||||
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')));
|
||||
|
||||
event('eloquent.created: App\Models\Payment', $payment);
|
||||
|
||||
if($invoices->sum('balance') > 0){
|
||||
|
||||
$invoice = $invoices->first();
|
||||
|
||||
return redirect()->route('client.invoice.show', ['invoice' => $invoice->hashed_id, 'hash' => $request->input('hash')]);
|
||||
}
|
||||
|
||||
if (property_exists($payment_hash->data, 'billing_context')) {
|
||||
$billing_subscription = \App\Models\Subscription::find($payment_hash->data->billing_context->subscription_id);
|
||||
|
||||
|
@ -33,7 +33,9 @@ class SubscriptionPlanSwitchController extends Controller
|
||||
{
|
||||
$amount = $recurring_invoice->subscription
|
||||
->service()
|
||||
->calculateUpgradePrice($recurring_invoice, $target);
|
||||
->calculateUpgradePriceV2($recurring_invoice, $target);
|
||||
|
||||
nlog("payment amount = {$amount}");
|
||||
/**
|
||||
* Null value here is a proxy for
|
||||
* denying the user a change plan option
|
||||
@ -42,6 +44,9 @@ class SubscriptionPlanSwitchController extends Controller
|
||||
render('subscriptions.denied');
|
||||
}
|
||||
|
||||
|
||||
$amount = max(0,$amount);
|
||||
|
||||
return render('subscriptions.switch', [
|
||||
'subscription' => $recurring_invoice->subscription,
|
||||
'recurring_invoice' => $recurring_invoice,
|
||||
|
@ -53,6 +53,16 @@ class SubscriptionPurchaseController extends Controller
|
||||
$this->setLocale($request->query('locale'));
|
||||
}
|
||||
|
||||
if(!auth()->guard('contact')->check() && $subscription->registration_required && $subscription->company->client_can_register) {
|
||||
|
||||
session()->put('url.intended', route('client.subscription.upgrade',['subscription' => $subscription->hashed_id]));
|
||||
|
||||
return redirect()->route('client.register', ['company_key' => $subscription->company->company_key]);
|
||||
}
|
||||
elseif(!auth()->guard('contact')->check() && $subscription->registration_required && ! $subscription->company->client_can_register) {
|
||||
return render('generic.subscription_blocked', ['account' => $subscription->company->account, 'company' => $subscription->company]);
|
||||
}
|
||||
|
||||
return view('billing-portal.purchasev2', [
|
||||
'subscription' => $subscription,
|
||||
'hash' => Str::uuid()->toString(),
|
||||
|
@ -521,7 +521,7 @@ class CompanyController extends BaseController
|
||||
$nmo->company = $other_company;
|
||||
$nmo->settings = $other_company->settings;
|
||||
$nmo->to_user = auth()->user();
|
||||
NinjaMailerJob::dispatch($nmo, true);
|
||||
(new NinjaMailerJob($nmo, true))->handle();
|
||||
|
||||
$company->delete();
|
||||
|
||||
|
@ -135,11 +135,45 @@ class ExpenseCategoryController extends BaseController
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param StoreExpenseCategoryRequest $request
|
||||
* @param StoreInvoiceRequest $request The request
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/expense_categories",
|
||||
* operationId="storeExpenseCategory",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Adds a expense category",
|
||||
* description="Adds an expense category to the system",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the saved invoice object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function store(StoreExpenseCategoryRequest $request)
|
||||
{
|
||||
|
@ -195,10 +195,10 @@ class PurchaseOrderController extends BaseController
|
||||
->fillDefaults()
|
||||
->triggeredActions($request)
|
||||
->save();
|
||||
|
||||
|
||||
event(new PurchaseOrderWasCreated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($purchase_order);
|
||||
return $this->itemResponse($purchase_order->fresh());
|
||||
}
|
||||
/**
|
||||
* Display the specified resource.
|
||||
|
@ -174,6 +174,8 @@ class BillingPortalPurchase extends Component
|
||||
*/
|
||||
public $company;
|
||||
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* Campaign reference.
|
||||
*
|
||||
@ -183,7 +185,11 @@ class BillingPortalPurchase extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->subscription = Subscription::with('company')->find($this->subscription);
|
||||
|
||||
$this->company = $this->subscription->company;
|
||||
|
||||
$this->quantity = 1;
|
||||
|
||||
@ -225,10 +231,10 @@ class BillingPortalPurchase extends Component
|
||||
|
||||
$this->steps['existing_user'] = false;
|
||||
|
||||
$contact = $this->createBlankClient();
|
||||
$this->contact = $this->createBlankClient();
|
||||
|
||||
if ($contact && $contact instanceof ClientContact) {
|
||||
$this->getPaymentMethods($contact);
|
||||
if ($this->contact && $this->contact instanceof ClientContact) {
|
||||
$this->getPaymentMethods($this->contact);
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,9 +271,6 @@ class BillingPortalPurchase extends Component
|
||||
}
|
||||
}
|
||||
|
||||
// nlog($this->subscription->group_settings->settings);
|
||||
// nlog($this->subscription->group_settings->settings->currency_id);
|
||||
|
||||
if(array_key_exists('currency_id', $this->request_data)) {
|
||||
|
||||
$currency = Cache::get('currencies')->filter(function ($item){
|
||||
|
@ -15,6 +15,7 @@ use App\DataMapper\ClientSettings;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Subscription\CleanStaleInvoiceOrder;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\ContactPasswordlessLogin;
|
||||
use App\Mail\Subscription\OtpCode;
|
||||
@ -120,7 +121,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $request_data;
|
||||
public $request_data = [];
|
||||
|
||||
/**
|
||||
* Instance of company.
|
||||
@ -129,6 +130,14 @@ class BillingPortalPurchasev2 extends Component
|
||||
*/
|
||||
public $company;
|
||||
|
||||
|
||||
/**
|
||||
* Instance of company.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $db;
|
||||
|
||||
/**
|
||||
* Campaign reference.
|
||||
*
|
||||
@ -151,10 +160,23 @@ class BillingPortalPurchasev2 extends Component
|
||||
public $valid_coupon = false;
|
||||
public $payable_invoices = [];
|
||||
public $payment_confirmed = false;
|
||||
|
||||
public $is_eligible = true;
|
||||
public $not_eligible_message = '';
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->subscription = Subscription::with('company')->find($this->subscription);
|
||||
|
||||
$this->company = $this->subscription->company;
|
||||
|
||||
if(auth()->guard('contact')->check()){
|
||||
$this->email = auth()->guard('contact')->user()->email;
|
||||
$this->contact = auth()->guard('contact')->user();
|
||||
$this->authenticated = true;
|
||||
$this->payment_started = true;
|
||||
}
|
||||
|
||||
$this->discount = 0;
|
||||
$this->sub_total = 0;
|
||||
@ -177,7 +199,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
$this->coupon = request()->query('coupon');
|
||||
$this->handleCoupon();
|
||||
}
|
||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
||||
elseif(isset($this->subscription->promo_code) && strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
||||
$this->price = $this->subscription->promo_price;
|
||||
}
|
||||
|
||||
@ -224,6 +246,8 @@ class BillingPortalPurchasev2 extends Component
|
||||
|
||||
public function resetEmail()
|
||||
{
|
||||
$this->resetErrorBag('login');
|
||||
$this->resetValidation('login');
|
||||
$this->email = null;
|
||||
}
|
||||
|
||||
@ -449,8 +473,6 @@ class BillingPortalPurchasev2 extends Component
|
||||
|
||||
$this->buildBundle();
|
||||
|
||||
nlog($this->bundle);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -489,9 +511,20 @@ nlog($this->bundle);
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleBeforePaymentEvents() :void
|
||||
public function handleBeforePaymentEvents() :self
|
||||
{
|
||||
|
||||
$eligibility_check = $this->subscription->service()->isEligible($this->contact);
|
||||
|
||||
if(is_array($eligibility_check) && $eligibility_check['message'] != 'Success'){
|
||||
|
||||
$this->is_eligible = false;
|
||||
$this->not_eligible_message = $eligibility_check['message'];
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->contact->client->id,
|
||||
'date' => now()->format('Y-m-d'),
|
||||
@ -501,19 +534,9 @@ nlog($this->bundle);
|
||||
]],
|
||||
'user_input_promo_code' => $this->coupon,
|
||||
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
||||
// 'quantity' => $this->quantity,
|
||||
|
||||
];
|
||||
|
||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
||||
|
||||
// if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
|
||||
// $this->steps['not_eligible'] = true;
|
||||
// $this->steps['not_eligible_message'] = $is_eligible['message'];
|
||||
// $this->steps['show_loading_bar'] = false;
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
$this->invoice = $this->subscription
|
||||
->service()
|
||||
->createInvoiceV2($this->bundle, $this->contact->client_id, $this->valid_coupon)
|
||||
@ -534,6 +557,9 @@ nlog($this->bundle);
|
||||
], now()->addMinutes(60));
|
||||
|
||||
$this->emit('beforePaymentEventsCompleted');
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function handleTrial()
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -23,26 +24,31 @@ class CreditsTable extends Component
|
||||
use WithPagination;
|
||||
use WithSorting;
|
||||
|
||||
public $per_page = 10;
|
||||
public int $per_page = 10;
|
||||
|
||||
public $company;
|
||||
public Company $company;
|
||||
|
||||
public string $db;
|
||||
|
||||
public int $company_id;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->company = Company::find($this->company_id);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Credit::query()
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||
->where('status_id', '<>', Credit::STATUS_DRAFT)
|
||||
->where('is_deleted', 0)
|
||||
->where(function ($query) {
|
||||
$query->whereDate('due_date', '>=', now())
|
||||
->orWhereNull('due_date');
|
||||
//->orWhere('due_date', '=', '');
|
||||
})
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
@ -31,21 +32,27 @@ class DocumentsTable extends Component
|
||||
{
|
||||
use WithPagination, WithSorting;
|
||||
|
||||
public $client;
|
||||
public Company $company;
|
||||
|
||||
public $per_page = 10;
|
||||
public Client $client;
|
||||
|
||||
public $company;
|
||||
public int $client_id;
|
||||
|
||||
public int $per_page = 10;
|
||||
|
||||
public string $tab = 'documents';
|
||||
|
||||
public string $db;
|
||||
|
||||
protected $query;
|
||||
|
||||
public function mount($client)
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->client = $client;
|
||||
$this->client = Client::with('company')->find($this->client_id);
|
||||
|
||||
$this->company = $this->client->company;
|
||||
|
||||
$this->query = $this->documents();
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Carbon\Carbon;
|
||||
@ -23,15 +24,21 @@ class InvoicesTable extends Component
|
||||
{
|
||||
use WithPagination, WithSorting;
|
||||
|
||||
public $per_page = 10;
|
||||
public int $per_page = 10;
|
||||
|
||||
public $status = [];
|
||||
public array $status = [];
|
||||
|
||||
public $company;
|
||||
public Company $company;
|
||||
|
||||
public int $company_id;
|
||||
|
||||
public string $db;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->company = Company::find($this->company_id);
|
||||
|
||||
$this->sort_asc = false;
|
||||
|
||||
|
@ -23,13 +23,11 @@ class PayNowDropdown extends Component
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount(int $total)
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->total = $total;
|
||||
|
||||
$this->methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($total);
|
||||
$this->methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($this->total);
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@ -3,7 +3,9 @@
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
@ -13,17 +15,23 @@ class PaymentMethodsTable extends Component
|
||||
use WithPagination;
|
||||
use WithSorting;
|
||||
|
||||
public $per_page = 10;
|
||||
public int $per_page = 10;
|
||||
|
||||
public $client;
|
||||
public Client $client;
|
||||
|
||||
public $company;
|
||||
public Company $company;
|
||||
|
||||
public function mount($client)
|
||||
public int $client_id;
|
||||
|
||||
public string $db;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->client = $client;
|
||||
$this->client = Client::with('company')->find($this->client_id);
|
||||
|
||||
$this->company = $this->client->company;
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -23,17 +24,19 @@ class PaymentsTable extends Component
|
||||
use WithSorting;
|
||||
use WithPagination;
|
||||
|
||||
public $per_page = 10;
|
||||
public int $per_page = 10;
|
||||
|
||||
public $user;
|
||||
public Company $company;
|
||||
|
||||
public $company;
|
||||
public int $company_id;
|
||||
|
||||
public string $db;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->user = auth()->user();
|
||||
$this->company = Company::find($this->company_id);
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -22,15 +23,27 @@ class QuotesTable extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
|
||||
public $per_page = 10;
|
||||
public int $per_page = 10;
|
||||
|
||||
public $status = [];
|
||||
public array $status = [];
|
||||
|
||||
public $company;
|
||||
public Company $company;
|
||||
|
||||
public $sort = 'status_id'; // Default sortBy. Feel free to change or pull from client/company settings.
|
||||
public string $sort = 'status_id';
|
||||
|
||||
public bool $sort_asc = true;
|
||||
|
||||
public int $company_id;
|
||||
|
||||
public string $db;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->company = Company::find($this->company_id);
|
||||
}
|
||||
|
||||
public $sort_asc = true;
|
||||
|
||||
public function sortBy($field)
|
||||
{
|
||||
@ -41,16 +54,11 @@ class QuotesTable extends Component
|
||||
$this->sort = $field;
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
|
||||
$query = Quote::query()
|
||||
->with('client.gateway_tokens', 'company', 'client.contacts')
|
||||
->with('client.contacts', 'company')
|
||||
->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc');
|
||||
|
||||
if (count($this->status) > 0) {
|
||||
|
@ -142,7 +142,7 @@ class SubscriptionPlanSwitch extends Component
|
||||
{
|
||||
$this->hide_button = true;
|
||||
|
||||
$response = $this->target->service()->createChangePlanCredit([
|
||||
$response = $this->target->service()->createChangePlanCreditV2([
|
||||
'recurring_invoice' => $this->recurring_invoice,
|
||||
'subscription' => $this->subscription,
|
||||
'target' => $this->target,
|
||||
|
@ -18,6 +18,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use stdClass;
|
||||
|
||||
class PasswordProtection
|
||||
@ -111,7 +112,18 @@ class PasswordProtection
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
elseif(auth()->user()->oauth_provider_id == 'apple')
|
||||
{
|
||||
|
||||
$user = Socialite::driver('apple')->userFromToken($request->header('X-API-OAUTH-PASSWORD'));
|
||||
|
||||
if($user && ($user->email == auth()->user()->email)){
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return response()->json($error, 412);
|
||||
|
@ -47,7 +47,7 @@ class RegisterRequest extends FormRequest
|
||||
|
||||
foreach ($rules as $field => $properties) {
|
||||
if ($field === 'email') {
|
||||
$rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:255', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]);
|
||||
$rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:191', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]);
|
||||
}
|
||||
|
||||
if ($field === 'current_password') {
|
||||
|
@ -110,7 +110,8 @@ class UpdateCompanyRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
$settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']);
|
||||
if(isset($settings['email_style_custom']))
|
||||
$settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']);
|
||||
|
||||
if (! $account->isFreeHostedClient()) {
|
||||
return $settings;
|
||||
|
@ -41,7 +41,7 @@ class StoreExpenseRequest extends Request
|
||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id);
|
||||
}
|
||||
|
||||
if (! empty($this->client_id)) {
|
||||
if ($this->client_id) {
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,10 @@ class UpdateExpenseRequest extends Request
|
||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id);
|
||||
}
|
||||
|
||||
if ($this->client_id) {
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
}
|
||||
|
||||
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
return $this->globalRules($rules);
|
||||
|
@ -43,6 +43,7 @@ class StoreRecurringExpenseRequest extends Request
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
}
|
||||
|
||||
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
$rules['frequency_id'] = 'required|integer|digits_between:1,12';
|
||||
$rules['tax_amount1'] = 'numeric';
|
||||
$rules['tax_amount2'] = 'numeric';
|
||||
@ -61,10 +62,6 @@ class StoreRecurringExpenseRequest extends Request
|
||||
$input['next_send_date_client'] = $input['next_send_date'];
|
||||
}
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||
$input['currency_id'] = (string) auth()->user()->company()->settings->currency_id;
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ class UpdateRecurringExpenseRequest extends Request
|
||||
$rules['tax_amount1'] = 'numeric';
|
||||
$rules['tax_amount2'] = 'numeric';
|
||||
$rules['tax_amount3'] = 'numeric';
|
||||
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
return $this->globalRules($rules);
|
||||
}
|
||||
@ -70,10 +71,6 @@ class UpdateRecurringExpenseRequest extends Request
|
||||
$input['next_send_date_client'] = $input['next_send_date'];
|
||||
}
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('documents', $input)) {
|
||||
unset($input['documents']);
|
||||
}
|
||||
|
80
app/Jobs/Subscription/CleanStaleInvoiceOrder.php
Normal file
80
app/Jobs/Subscription/CleanStaleInvoiceOrder.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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\Jobs\Subscription;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CleanStaleInvoiceOrder implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param int invoice_id
|
||||
* @param string $db
|
||||
*/
|
||||
public function __construct(){}
|
||||
|
||||
/**
|
||||
* @param InvoiceRepository $repo
|
||||
* @return void
|
||||
*/
|
||||
public function handle(InvoiceRepository $repo) : void
|
||||
{
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
|
||||
Invoice::query()
|
||||
->withTrashed()
|
||||
->where('is_proforma', 1)
|
||||
->where('created_at', '<', now()->subHour())
|
||||
->cursor()
|
||||
->each(function ($invoice) use ($repo) {
|
||||
$invoice->is_proforma = false;
|
||||
$repo->delete($invoice);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
foreach (MultiDB::$dbs as $db)
|
||||
{
|
||||
|
||||
MultiDB::setDB($db);
|
||||
|
||||
Invoice::query()
|
||||
->withTrashed()
|
||||
->where('is_proforma', 1)
|
||||
->where('created_at', '<', now()->subHour())
|
||||
->cursor()
|
||||
->each(function ($invoice) use ($repo) {
|
||||
$invoice->is_proforma = false;
|
||||
$repo->delete($invoice);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function failed($exception = null)
|
||||
{
|
||||
}
|
||||
}
|
@ -21,12 +21,10 @@ use App\Notifications\Admin\EntitySentNotification;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class PurchaseOrderAcceptedNotification implements ShouldQueue
|
||||
class PurchaseOrderAcceptedListener implements ShouldQueue
|
||||
{
|
||||
use UserNotifies;
|
||||
|
||||
public $delay = 5;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
81
app/Listeners/PurchaseOrder/PurchaseOrderCreatedListener.php
Normal file
81
app/Listeners/PurchaseOrder/PurchaseOrderCreatedListener.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* PurchaseOrder Ninja (https://purchase_orderninja.com).
|
||||
*
|
||||
* @link https://github.com/purchase_orderninja/purchase_orderninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. PurchaseOrder Ninja LLC (https://purchase_orderninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\PurchaseOrder;
|
||||
|
||||
use App\Events\PurchaseOrder\PurchaseOrderWasCreated;
|
||||
use App\Jobs\Mail\NinjaMailer;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntityCreatedObject;
|
||||
use App\Notifications\Admin\EntitySentNotification;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class PurchaseOrderCreatedListener implements ShouldQueue
|
||||
{
|
||||
use UserNotifies;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(PurchaseOrderWasCreated $event)
|
||||
{
|
||||
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$first_notification_sent = true;
|
||||
|
||||
$purchase_order = $event->purchase_order;
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($purchase_order, 'purchase_order'))->build());
|
||||
$nmo->company = $purchase_order->company;
|
||||
$nmo->settings = $purchase_order->company->settings;
|
||||
|
||||
/* We loop through each user and determine whether they need to be notified */
|
||||
foreach ($event->company->company_users as $company_user) {
|
||||
|
||||
/* The User */
|
||||
$user = $company_user->user;
|
||||
|
||||
if (! $user) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This is only here to handle the alternate message channels - ie Slack */
|
||||
// $notification = new EntitySentNotification($event->invitation, 'purchase_order');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_created', 'purchase_order_created_all']);
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
$nmo->to_user = $user;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
/* This prevents more than one notification being sent */
|
||||
$first_notification_sent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* PurchaseOrder Ninja (https://purchase_orderninja.com).
|
||||
*
|
||||
* @link https://github.com/purchase_orderninja/purchase_orderninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. PurchaseOrder Ninja LLC (https://purchase_orderninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\PurchaseOrder;
|
||||
|
||||
use App\Jobs\Mail\NinjaMailer;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntitySentObject;
|
||||
use App\Notifications\Admin\EntitySentNotification;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class PurchaseOrderEmailedNotification implements ShouldQueue
|
||||
{
|
||||
use UserNotifies;
|
||||
|
||||
public $delay = 5;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$first_notification_sent = true;
|
||||
|
||||
$purchase_order = $event->invitation->purchase_order->fresh();
|
||||
$purchase_order->last_sent_date = now();
|
||||
$purchase_order->saveQuietly();
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'purchase_order', 'purchase_order'))->build());
|
||||
$nmo->company = $purchase_order->company;
|
||||
$nmo->settings = $purchase_order->company->settings;
|
||||
|
||||
/* We loop through each user and determine whether they need to be notified */
|
||||
foreach ($event->invitation->company->company_users as $company_user) {
|
||||
|
||||
/* The User */
|
||||
$user = $company_user->user;
|
||||
|
||||
/* This is only here to handle the alternate message channels - ie Slack */
|
||||
// $notification = new EntitySentNotification($event->invitation, 'purchase_order');
|
||||
|
||||
/* Returns an array of notification methods */
|
||||
$methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'purchase_order', ['all_notifications', 'purchase_order_sent', 'purchase_order_sent_all']);
|
||||
|
||||
/* If one of the methods is email then we fire the EntitySentMailer */
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
$nmo->to_user = $user;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
/* This prevents more than one notification being sent */
|
||||
$first_notification_sent = false;
|
||||
}
|
||||
|
||||
/* Override the methods in the Notification Class */
|
||||
// $notification->method = $methods;
|
||||
|
||||
// Notify on the alternate channels
|
||||
// $user->notify($notification);
|
||||
}
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@ class AutoBillingFailureObject
|
||||
/* Set customized translations _NOW_ */
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$this->$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
|
||||
$this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
|
@ -13,6 +13,7 @@ namespace App\Mail\Admin;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Number;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use stdClass;
|
||||
|
||||
@ -38,7 +39,11 @@ class EntityCreatedObject
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
public function build()
|
||||
/**
|
||||
* @return stdClass
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function build() :stdClass
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
/* Init a new copy of the translator*/
|
||||
@ -47,26 +52,64 @@ class EntityCreatedObject
|
||||
App::setLocale($this->entity->company->getLocale());
|
||||
/* Set customized translations _NOW_ */
|
||||
$t->replace(Ninja::transformTranslations($this->entity->company->settings));
|
||||
|
||||
$this->entity->load('client.country', 'client.company');
|
||||
$this->client = $this->entity->client;
|
||||
$this->setTemplate();
|
||||
$this->company = $this->entity->company;
|
||||
|
||||
$this->setTemplate();
|
||||
if($this->entity_type == 'purchase_order')
|
||||
{
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
$this->entity->load('vendor.company');
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = Number::formatMoney($this->entity->amount, $this->entity->vendor);
|
||||
|
||||
$mail_obj->subject = ctrans($this->template_subject,
|
||||
[
|
||||
'vendor' => $this->entity->vendor->present()->name(),
|
||||
'purchase_order' => $this->entity->number,
|
||||
]
|
||||
);
|
||||
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
$mail_obj->data = [
|
||||
'title' => $mail_obj->subject,
|
||||
'message' => ctrans($this->template_body,
|
||||
[
|
||||
'amount' => $mail_obj->amount,
|
||||
'vendor' => $this->entity->vendor->present()->name(),
|
||||
'purchase_order' => $this->entity->number,
|
||||
]
|
||||
),
|
||||
'url' => $this->entity->invitations()->first()->getAdminLink(),
|
||||
'button' => ctrans("texts.view_{$this->entity_type}"),
|
||||
'signature' => $this->company->settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->company->settings,
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$this->entity->load('client.country', 'client.company');
|
||||
$this->client = $this->entity->client;
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->entity->company->company_key;
|
||||
|
||||
}
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
||||
private function setTemplate()
|
||||
{
|
||||
// nlog($this->template);
|
||||
|
||||
switch ($this->entity_type) {
|
||||
case 'invoice':
|
||||
@ -81,7 +124,10 @@ class EntityCreatedObject
|
||||
$this->template_subject = 'texts.notification_credit_created_subject';
|
||||
$this->template_body = 'texts.notification_credit_created_body';
|
||||
break;
|
||||
|
||||
case 'purchase_order':
|
||||
$this->template_subject = 'texts.notification_purchase_order_created_subject';
|
||||
$this->template_body = 'texts.notification_purchase_order_created_body';
|
||||
break;
|
||||
default:
|
||||
$this->template_subject = 'texts.notification_invoice_created_subject';
|
||||
$this->template_body = 'texts.notification_invoice_created_body';
|
||||
@ -89,7 +135,7 @@ class EntityCreatedObject
|
||||
}
|
||||
}
|
||||
|
||||
private function getAmount()
|
||||
private function getAmount()
|
||||
{
|
||||
return Number::formatMoney($this->entity->amount, $this->entity->client);
|
||||
}
|
||||
|
@ -58,13 +58,48 @@ class EntitySentObject
|
||||
|
||||
$this->setTemplate();
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
if($this->template == 'purchase_order')
|
||||
{
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = Number::formatMoney($this->entity->amount, $this->entity->vendor);
|
||||
$mail_obj->subject = ctrans($this->template_subject,
|
||||
[
|
||||
'vendor' => $this->contact->vendor->present()->name(),
|
||||
'purchase_order' => $this->entity->number,
|
||||
]
|
||||
);
|
||||
$mail_obj->data = [
|
||||
'title' => $mail_obj->subject,
|
||||
'message' => ctrans($this->template_body,
|
||||
[
|
||||
'amount' => $mail_obj->amount,
|
||||
'vendor' => $this->contact->vendor->present()->name(),
|
||||
'purchase_order' => $this->entity->number,
|
||||
]
|
||||
),
|
||||
'url' => $this->invitation->getAdminLink(),
|
||||
'button' => ctrans("texts.view_{$this->entity_type}"),
|
||||
'signature' => $this->company->settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->company->settings,
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
];
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$mail_obj = new stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
}
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
||||
@ -101,7 +136,10 @@ class EntitySentObject
|
||||
$this->template_subject = 'texts.notification_credit_sent_subject';
|
||||
$this->template_body = 'texts.notification_credit_sent';
|
||||
break;
|
||||
|
||||
case 'purchase_order':
|
||||
$this->template_subject = 'texts.notification_purchase_order_sent_subject';
|
||||
$this->template_body = 'texts.notification_purchase_order_sent';
|
||||
break;
|
||||
default:
|
||||
$this->template_subject = 'texts.notification_invoice_sent_subject';
|
||||
$this->template_body = 'texts.notification_invoice_sent';
|
||||
|
@ -129,6 +129,7 @@ class Company extends BaseModel
|
||||
'invoice_task_lock',
|
||||
'convert_payment_currency',
|
||||
'convert_expense_currency',
|
||||
'notify_vendor_when_paid',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
@ -138,6 +139,7 @@ class Company extends BaseModel
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_proforma' => 'bool',
|
||||
'country_id' => 'string',
|
||||
'custom_fields' => 'object',
|
||||
'settings' => 'object',
|
||||
|
@ -117,6 +117,11 @@ class Vendor extends BaseModel
|
||||
|
||||
}
|
||||
|
||||
public function timezone()
|
||||
{
|
||||
return $this->company->timezone();
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
|
@ -325,15 +325,6 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
$invoice->service()->toggleFeesPaid()->save();
|
||||
}
|
||||
|
||||
$transaction = [
|
||||
'invoice' => $invoice->transaction_event(),
|
||||
'payment' => [],
|
||||
'client' => $invoice->client->transaction_event(),
|
||||
'credit' => [],
|
||||
'metadata' => [],
|
||||
];
|
||||
|
||||
// TransactionLog::dispatch(TransactionEvent::INVOICE_FEE_APPLIED, $transaction, $invoice->company->db);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -124,18 +124,20 @@ class CreditCard implements MethodInterface
|
||||
}
|
||||
} catch (CheckoutApiException $e) {
|
||||
// API error
|
||||
$request_id = $e->request_id;
|
||||
$http_status_code = $e->http_status_code;
|
||||
$request_id = $e->request_id ?: '';
|
||||
$http_status_code = $e->http_status_code ?: '';
|
||||
$error_details = $e->error_details;
|
||||
|
||||
if(is_array($error_details)) {
|
||||
$error_details = end($e->error_details['error_codes']);
|
||||
}
|
||||
|
||||
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
|
||||
$human_exception = $error_details ? $error_details : $e->getMessage();
|
||||
|
||||
$human_exception = "{$human_exception} - Request ID: {$request_id}";
|
||||
|
||||
throw new PaymentFailed($human_exception, $http_status_code);
|
||||
|
||||
throw new PaymentFailed($human_exception);
|
||||
} catch (CheckoutArgumentException $e) {
|
||||
// Bad arguments
|
||||
|
||||
@ -145,9 +147,9 @@ class CreditCard implements MethodInterface
|
||||
$error_details = end($e->error_details['error_codes']);
|
||||
}
|
||||
|
||||
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
|
||||
$human_exception = $error_details ? $error_details : $e->getMessage();
|
||||
|
||||
throw new PaymentFailed($human_exception);
|
||||
throw new PaymentFailed($human_exception, 422);
|
||||
} catch (CheckoutAuthorizationException $e) {
|
||||
// Bad Invalid authorization
|
||||
|
||||
@ -157,9 +159,9 @@ class CreditCard implements MethodInterface
|
||||
$error_details = end($e->error_details['error_codes']);
|
||||
}
|
||||
|
||||
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
|
||||
$human_exception = $error_details ? $error_details : $e->getMessage();
|
||||
|
||||
throw new PaymentFailed($human_exception);
|
||||
throw new PaymentFailed($human_exception, 401);
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +230,7 @@ class CreditCard implements MethodInterface
|
||||
private function completePayment($paymentRequest, PaymentResponseRequest $request)
|
||||
{
|
||||
$paymentRequest->amount = $this->checkout->payment_hash->data->value;
|
||||
$paymentRequest->reference = $this->checkout->getDescription();
|
||||
$paymentRequest->reference = substr($this->checkout->getDescription(),0 , 49);
|
||||
$paymentRequest->customer = $this->checkout->getCustomer();
|
||||
$paymentRequest->metadata = ['udf1' => 'Invoice Ninja'];
|
||||
$paymentRequest->currency = $this->checkout->client->getCurrencyCode();
|
||||
|
@ -87,6 +87,9 @@ trait Utilities
|
||||
|
||||
$error_message = '';
|
||||
|
||||
nlog("checkout failure");
|
||||
nlog($_payment);
|
||||
|
||||
if (is_array($_payment) && array_key_exists('actions', $_payment) && array_key_exists('response_summary', end($_payment['actions']))) {
|
||||
$error_message = end($_payment['actions'])['response_summary'];
|
||||
} elseif (is_array($_payment) && array_key_exists('status', $_payment)) {
|
||||
|
@ -627,18 +627,16 @@ class StripePaymentDriver extends BaseDriver
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request)
|
||||
{
|
||||
// Allow app to catch up with webhook request.
|
||||
sleep(2);
|
||||
|
||||
//payment_intent.succeeded - this will confirm or cancel the payment
|
||||
if ($request->type === 'payment_intent.succeeded') {
|
||||
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(2, 10)));
|
||||
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(5, 10)));
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
if (in_array($request->type, ['payment_intent.payment_failed', 'charge.failed'])) {
|
||||
PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(2, 10)));
|
||||
PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(5, 10)));
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
@ -157,15 +157,14 @@ use App\Listeners\Credit\CreditRestoredActivity;
|
||||
use App\Listeners\Credit\CreditViewedActivity;
|
||||
use App\Listeners\Document\DeleteCompanyDocuments;
|
||||
use App\Listeners\Invoice\CreateInvoiceActivity;
|
||||
use App\Listeners\Invoice\CreateInvoiceHtmlBackup;
|
||||
use App\Listeners\Invoice\CreateInvoicePdf;
|
||||
use App\Listeners\Invoice\InvoiceArchivedActivity;
|
||||
use App\Listeners\Invoice\InvoiceCancelledActivity;
|
||||
use App\Listeners\Invoice\InvoiceCreatedNotification;
|
||||
use App\Listeners\Invoice\InvoiceDeletedActivity;
|
||||
use App\Listeners\Invoice\InvoiceEmailActivity;
|
||||
use App\Listeners\Invoice\InvoiceEmailedNotification;
|
||||
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
|
||||
use App\Listeners\Invoice\InvoiceEmailedNotification;
|
||||
use App\Listeners\Invoice\InvoiceFailedEmailNotification;
|
||||
use App\Listeners\Invoice\InvoicePaidActivity;
|
||||
use App\Listeners\Invoice\InvoiceReminderEmailActivity;
|
||||
@ -175,18 +174,21 @@ use App\Listeners\Invoice\InvoiceViewedActivity;
|
||||
use App\Listeners\Invoice\UpdateInvoiceActivity;
|
||||
use App\Listeners\Mail\MailSentListener;
|
||||
use App\Listeners\Misc\InvitationViewedListener;
|
||||
use App\Listeners\Payment\PaymentEmailedActivity;
|
||||
use App\Listeners\Payment\PaymentEmailFailureActivity;
|
||||
use App\Listeners\Payment\PaymentEmailedActivity;
|
||||
use App\Listeners\Payment\PaymentNotification;
|
||||
use App\Listeners\Payment\PaymentRestoredActivity;
|
||||
use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedNotification;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedListener;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderArchivedActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderCreatedListener;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderDeletedActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderEmailActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderEmailedNotification;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderRestoredActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderViewedActivity;
|
||||
use App\Listeners\PurchaseOrder\PurchaseOrderViewedNotification;
|
||||
use App\Listeners\PurchaseOrder\UpdatePurchaseOrderActivity;
|
||||
use App\Listeners\Quote\QuoteApprovedActivity;
|
||||
use App\Listeners\Quote\QuoteApprovedNotification;
|
||||
@ -219,8 +221,8 @@ use App\Listeners\User\ArchivedUserActivity;
|
||||
use App\Listeners\User\CreatedUserActivity;
|
||||
use App\Listeners\User\DeletedUserActivity;
|
||||
use App\Listeners\User\RestoredUserActivity;
|
||||
use App\Listeners\User\UpdatedUserActivity;
|
||||
use App\Listeners\User\UpdateUserLastLogin;
|
||||
use App\Listeners\User\UpdatedUserActivity;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
@ -398,7 +400,6 @@ class EventServiceProvider extends ServiceProvider
|
||||
],
|
||||
//Invoices
|
||||
InvoiceWasMarkedSent::class => [
|
||||
CreateInvoiceHtmlBackup::class,
|
||||
],
|
||||
InvoiceWasUpdated::class => [
|
||||
UpdateInvoiceActivity::class,
|
||||
@ -458,12 +459,14 @@ class EventServiceProvider extends ServiceProvider
|
||||
],
|
||||
PurchaseOrderWasCreated::class => [
|
||||
CreatePurchaseOrderActivity::class,
|
||||
PurchaseOrderCreatedListener::class,
|
||||
],
|
||||
PurchaseOrderWasDeleted::class => [
|
||||
PurchaseOrderDeletedActivity::class,
|
||||
],
|
||||
PurchaseOrderWasEmailed::class => [
|
||||
PurchaseOrderEmailActivity::class,
|
||||
PurchaseOrderEmailedNotification::class,
|
||||
],
|
||||
PurchaseOrderWasRestored::class => [
|
||||
PurchaseOrderRestoredActivity::class,
|
||||
@ -475,8 +478,8 @@ class EventServiceProvider extends ServiceProvider
|
||||
PurchaseOrderViewedActivity::class,
|
||||
],
|
||||
PurchaseOrderWasAccepted::class => [
|
||||
PurchaseOrderAcceptedListener::class,
|
||||
PurchaseOrderAcceptedActivity::class,
|
||||
PurchaseOrderAcceptedNotification::class,
|
||||
],
|
||||
CompanyDocumentsDeleted::class => [
|
||||
DeleteCompanyDocuments::class,
|
||||
|
@ -21,6 +21,7 @@ use App\Models\Invoice;
|
||||
use App\Services\Bank\BankService;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
@ -29,7 +30,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class BankMatchingService implements ShouldQueue
|
||||
class BankMatchingService implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
@ -37,13 +38,10 @@ class BankMatchingService implements ShouldQueue
|
||||
|
||||
protected $db;
|
||||
|
||||
protected $middleware_key;
|
||||
|
||||
public function __construct($company_id, $db)
|
||||
{
|
||||
$this->company_id = $company_id;
|
||||
$this->db = $db;
|
||||
$this->middleware_key = "bank_match_rate:{$this->company_id}";
|
||||
}
|
||||
|
||||
public function handle() :void
|
||||
@ -62,8 +60,14 @@ class BankMatchingService implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
public function middleware()
|
||||
/**
|
||||
* The unique ID of the job.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function uniqueId()
|
||||
{
|
||||
return [new WithoutOverlapping($this->middleware_key)];
|
||||
return (string)$this->company_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class InstantPayment
|
||||
|
||||
public function run()
|
||||
{
|
||||
|
||||
nlog($this->request->all());
|
||||
$is_credit_payment = false;
|
||||
|
||||
$tokens = [];
|
||||
@ -221,6 +221,9 @@ class InstantPayment
|
||||
if ($this->request->query('hash')) {
|
||||
$hash_data['billing_context'] = Cache::get($this->request->query('hash'));
|
||||
}
|
||||
elseif($this->request->hash){
|
||||
$hash_data['billing_context'] = Cache::get($this->request->hash);
|
||||
}
|
||||
|
||||
$payment_hash = new PaymentHash;
|
||||
$payment_hash->hash = Str::random(32);
|
||||
|
@ -82,9 +82,6 @@ class MarkInvoiceDeleted extends AbstractService
|
||||
{
|
||||
//if total payments = adjustment amount - that means we need to delete the payments as well.
|
||||
|
||||
nlog($this->adjustment_amount);
|
||||
nlog($this->total_payments);
|
||||
|
||||
if ($this->adjustment_amount == $this->total_payments)
|
||||
$this->invoice->payments()->update(['payments.deleted_at' => now(), 'payments.is_deleted' => true]);
|
||||
|
||||
|
@ -140,6 +140,39 @@ class PaymentService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function applyCreditsToInvoice($invoice)
|
||||
{
|
||||
|
||||
$amount = $invoice->amount;
|
||||
|
||||
$credits = $invoice->client
|
||||
->service()
|
||||
->getCredits();
|
||||
|
||||
foreach ($credits as $credit) {
|
||||
//starting invoice balance
|
||||
$invoice_balance = $invoice->balance;
|
||||
|
||||
//credit payment applied
|
||||
$credit->service()->applyPayment($invoice, $amount, $this->payment);
|
||||
|
||||
//amount paid from invoice calculated
|
||||
$remaining_balance = ($invoice_balance - $invoice->fresh()->balance);
|
||||
|
||||
//reduce the amount to be paid on the invoice from the NEXT credit
|
||||
$amount -= $remaining_balance;
|
||||
|
||||
//break if the invoice is no longer PAYABLE OR there is no more amount to be applied
|
||||
if (! $invoice->isPayable() || (int) $amount == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->payment->saveQuietly();
|
||||
|
@ -80,11 +80,19 @@ class UpdateInvoicePayment
|
||||
->clearPartial()
|
||||
->updateStatus()
|
||||
->touchPdf()
|
||||
->workFlow()
|
||||
->save();
|
||||
|
||||
$invoice->service()
|
||||
->workFlow()
|
||||
->save();
|
||||
if($invoice->is_proforma)
|
||||
{
|
||||
$invoice->number = '';
|
||||
$invoice->is_proforma = false;
|
||||
|
||||
$invoice->service()
|
||||
->applyNumber()
|
||||
->save();
|
||||
}
|
||||
|
||||
|
||||
/* Updates the company ledger */
|
||||
$this->payment
|
||||
@ -101,17 +109,6 @@ class UpdateInvoicePayment
|
||||
|
||||
$this->payment->applied += $paid_amount;
|
||||
|
||||
$transaction = [
|
||||
'invoice' => $invoice->transaction_event(),
|
||||
'payment' => $this->payment->transaction_event(),
|
||||
'client' => $client->transaction_event(),
|
||||
'credit' => [],
|
||||
'metadata' => [],
|
||||
];
|
||||
|
||||
// TransactionLog::dispatch(TransactionEvent::GATEWAY_PAYMENT_MADE, $transaction, $invoice->company->db);
|
||||
|
||||
|
||||
});
|
||||
|
||||
/* Remove the event updater from within the loop to prevent race conditions */
|
||||
|
@ -40,7 +40,7 @@ class MarkSent
|
||||
->service()
|
||||
->setStatus(PurchaseOrder::STATUS_SENT)
|
||||
->applyNumber()
|
||||
// ->adjustBalance($this->purchase_order->amount)
|
||||
->adjustBalance($this->purchase_order->amount) //why was this commented out previously?
|
||||
// ->touchPdf()
|
||||
->save();
|
||||
|
||||
|
@ -39,7 +39,8 @@ class PurchaseOrderExpense
|
||||
$expense->uses_inclusive_taxes = $this->purchase_order->uses_inclusive_taxes;
|
||||
$expense->calculate_tax_by_amount = true;
|
||||
$expense->private_notes = ctrans('texts.purchase_order_number_short') . " " . $this->purchase_order->number;
|
||||
|
||||
$expense->currency_id = $this->purchase_order->vendor->currency_id;
|
||||
|
||||
$line_items = $this->purchase_order->line_items;
|
||||
|
||||
$expense->public_notes = '';
|
||||
|
@ -97,6 +97,13 @@ class PurchaseOrderService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function adjustBalance($adjustment)
|
||||
{
|
||||
$this->purchase_order->balance += $adjustment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function touchPdf($force = false)
|
||||
{
|
||||
try {
|
||||
|
@ -15,6 +15,7 @@ use App\DataMapper\InvoiceItem;
|
||||
use App\Factory\CreditFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceToRecurringInvoiceFactory;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Jobs\Mail\NinjaMailer;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
@ -28,6 +29,7 @@ use App\Models\ClientContact;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\Product;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Subscription;
|
||||
@ -89,11 +91,18 @@ class SubscriptionService
|
||||
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
||||
$recurring_invoice->auto_bill = $this->subscription->auto_bill;
|
||||
|
||||
|
||||
/* Start the recurring service */
|
||||
$recurring_invoice->service()
|
||||
->start()
|
||||
->save();
|
||||
|
||||
//update the invoice and attach to the recurring invoice!!!!!
|
||||
$invoice = Invoice::find($payment_hash->fee_invoice_id);
|
||||
$invoice->recurring_id = $recurring_invoice->id;
|
||||
$invoice->is_proforma = false;
|
||||
$invoice->save();
|
||||
|
||||
//execute any webhooks
|
||||
$context = [
|
||||
'context' => 'recurring_purchase',
|
||||
@ -101,7 +110,7 @@ class SubscriptionService
|
||||
'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id),
|
||||
'client' => $recurring_invoice->client->hashed_id,
|
||||
'subscription' => $this->subscription->hashed_id,
|
||||
'contact' => auth()->guard('contact')->user() ? auth()->guard('contact')->user()->hashed_id : $recurring_invoice->client->contacts()->first()->hashed_id,
|
||||
'contact' => auth()->guard('contact')->user() ? auth()->guard('contact')->user()->hashed_id : $recurring_invoice->client->contacts()->whereNotNull('email')->first()->hashed_id,
|
||||
'account_key' => $recurring_invoice->client->custom_value2,
|
||||
];
|
||||
|
||||
@ -217,23 +226,70 @@ class SubscriptionService
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function calculateUpgradePriceV2(RecurringInvoice $recurring_invoice, Subscription $target) :?float
|
||||
{
|
||||
|
||||
$outstanding_credit = 0;
|
||||
|
||||
$use_credit_setting = $recurring_invoice->client->getSetting('use_credits_payment');
|
||||
|
||||
$last_invoice = Invoice::query()
|
||||
->where('recurring_id', $recurring_invoice->id)
|
||||
->where('is_deleted', 0)
|
||||
->where('status_id', Invoice::STATUS_PAID)
|
||||
->first();
|
||||
|
||||
$refund = $this->calculateProRataRefundForSubscription($last_invoice);
|
||||
|
||||
if($use_credit_setting != 'off')
|
||||
{
|
||||
|
||||
$outstanding_credit = Credit::query()
|
||||
->where('client_id', $recurring_invoice->client_id)
|
||||
->whereIn('status_id', [Credit::STATUS_SENT,Credit::STATUS_PARTIAL])
|
||||
->where('is_deleted', 0)
|
||||
->where('balance', '>', 0)
|
||||
->sum('balance');
|
||||
|
||||
}
|
||||
|
||||
nlog("{$target->price} - {$refund} - {$outstanding_credit}");
|
||||
|
||||
return $target->price - $refund - $outstanding_credit;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an upgrade price when moving between plans
|
||||
*
|
||||
* However we only allow people to move between plans
|
||||
* if their account is in good standing.
|
||||
*
|
||||
* @param RecurringInvoice $recurring_invoice
|
||||
* @param Subscription $target
|
||||
* @deprecated in favour of calculateUpgradePriceV2
|
||||
* @return float
|
||||
*/
|
||||
public function calculateUpgradePrice(RecurringInvoice $recurring_invoice, Subscription $target) :?float
|
||||
{
|
||||
//calculate based on daily prices
|
||||
|
||||
//calculate based on daily prices
|
||||
$current_amount = $recurring_invoice->amount;
|
||||
$currency_frequency = $recurring_invoice->frequency_id;
|
||||
|
||||
$outstanding = $recurring_invoice->invoices()
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0);
|
||||
$outstanding = Invoice::query()
|
||||
->where('recurring_id', $recurring_invoice->id)
|
||||
->where('is_deleted', 0)
|
||||
->where('is_proforma',0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0);
|
||||
|
||||
$outstanding_amounts = $outstanding->sum('balance');
|
||||
|
||||
$outstanding_invoice = Invoice::where('subscription_id', $this->subscription->id)
|
||||
->where('client_id', $recurring_invoice->client_id)
|
||||
$outstanding_invoice = Invoice::where('client_id', $recurring_invoice->client_id)
|
||||
->where('is_deleted', 0)
|
||||
->where('is_proforma',0)
|
||||
->where('subscription_id', $this->subscription->id)
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
@ -242,6 +298,7 @@ class SubscriptionService
|
||||
|
||||
$outstanding_invoice = Credit::where('subscription_id', $this->subscription->id)
|
||||
->where('client_id', $recurring_invoice->client_id)
|
||||
->where('is_proforma',0)
|
||||
->where('is_deleted', 0)
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
@ -289,13 +346,9 @@ class SubscriptionService
|
||||
|
||||
$days_in_frequency = $this->getDaysInFrequency();
|
||||
|
||||
$pro_rata_refund = round((($days_in_frequency - $days_of_subscription_used)/$days_in_frequency) * $this->subscription->price ,2);
|
||||
$pro_rata_refund = round((($days_in_frequency - $days_of_subscription_used)/$days_in_frequency) * $invoice->amount ,2);
|
||||
|
||||
// nlog("days in frequency = {$days_in_frequency} - days of subscription used {$days_of_subscription_used}");
|
||||
// nlog("invoice amount = {$invoice->amount}");
|
||||
// nlog("pro rata refund = {$pro_rata_refund}");
|
||||
|
||||
return $pro_rata_refund;
|
||||
return max(0, $pro_rata_refund);
|
||||
|
||||
}
|
||||
|
||||
@ -323,10 +376,6 @@ class SubscriptionService
|
||||
|
||||
$pro_rata_refund = round((($days_in_frequency - $days_of_subscription_used)/$days_in_frequency) * $invoice->amount ,2);
|
||||
|
||||
// nlog("days in frequency = {$days_in_frequency} - days of subscription used {$days_of_subscription_used}");
|
||||
// nlog("invoice amount = {$invoice->amount}");
|
||||
// nlog("pro rata refund = {$pro_rata_refund}");
|
||||
|
||||
return $pro_rata_refund;
|
||||
|
||||
}
|
||||
@ -353,7 +402,6 @@ class SubscriptionService
|
||||
|
||||
$days_of_subscription_used = $start_date->diffInDays($current_date);
|
||||
|
||||
// $days_in_frequency = $this->getDaysInFrequency();
|
||||
$days_in_frequency = $invoice->subscription->service()->getDaysInFrequency();
|
||||
|
||||
$ratio = ($days_in_frequency - $days_of_subscription_used)/$days_in_frequency;
|
||||
@ -406,10 +454,76 @@ class SubscriptionService
|
||||
return $pro_rata_charge;
|
||||
}
|
||||
|
||||
/**
|
||||
* This entry point assumes the user does not have to make a
|
||||
* payment for the service.
|
||||
*
|
||||
* In this case, we generate a credit note for the old service
|
||||
* Generate a new invoice for the new service
|
||||
* Apply credits to the invoice
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function createChangePlanCreditV2($data)
|
||||
{
|
||||
/* Init vars */
|
||||
$recurring_invoice = $data['recurring_invoice'];
|
||||
$old_subscription = $data['subscription'];
|
||||
$target_subscription = $data['target'];
|
||||
|
||||
$pro_rata_charge_amount = 0;
|
||||
$pro_rata_refund_amount = 0;
|
||||
$is_credit = false;
|
||||
$credit = false;
|
||||
|
||||
/* Get last invoice */
|
||||
$last_invoice = Invoice::where('subscription_id', $recurring_invoice->subscription_id)
|
||||
->where('client_id', $recurring_invoice->client_id)
|
||||
->where('is_proforma',0)
|
||||
->where('is_deleted', 0)
|
||||
->where('status_id', Invoice::STATUS_PAID)
|
||||
->withTrashed()
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
if($this->calculateProRataRefundForSubscription($last_invoice) > 0)
|
||||
$credit = $this->createCredit($last_invoice, $target_subscription, false);
|
||||
|
||||
$new_recurring_invoice = $this->createNewRecurringInvoice($recurring_invoice);
|
||||
|
||||
$invoice = $this->changePlanInvoice($target_subscription, $recurring_invoice->client_id);
|
||||
$invoice->recurring_id = $new_recurring_invoice->id;
|
||||
$invoice->save();
|
||||
|
||||
$payment = PaymentFactory::create($invoice->company_id, $invoice->user_id, $invoice->client_id);
|
||||
$payment->type_id = PaymentType::CREDIT;
|
||||
$payment->client_id = $invoice->client_id;
|
||||
$payment->is_manual = true;
|
||||
$payment->save();
|
||||
|
||||
$payment->service()->applyCreditsToInvoice($invoice);
|
||||
|
||||
$context = [
|
||||
'context' => 'change_plan',
|
||||
'recurring_invoice' => $new_recurring_invoice->hashed_id,
|
||||
'credit' => $credit ? $credit->hashed_id : null,
|
||||
'client' => $new_recurring_invoice->client->hashed_id,
|
||||
'subscription' => $target_subscription->hashed_id,
|
||||
'contact' => auth()->guard('contact')->user()->hashed_id,
|
||||
'account_key' => $new_recurring_invoice->client->custom_value2,
|
||||
];
|
||||
|
||||
$response = $this->triggerWebhook($context);
|
||||
|
||||
return '/client/recurring_invoices/'.$new_recurring_invoice->hashed_id;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* When downgrading, we may need to create
|
||||
* a credit
|
||||
*
|
||||
* @deprecated in favour of createChangePlanCreditV2
|
||||
* @param array $data
|
||||
*/
|
||||
public function createChangePlanCredit($data)
|
||||
@ -663,10 +777,10 @@ class SubscriptionService
|
||||
$credit = CreditFactory::create($this->subscription->company_id, $this->subscription->user_id);
|
||||
$credit->date = now()->format('Y-m-d');
|
||||
$credit->subscription_id = $this->subscription->id;
|
||||
|
||||
$line_items = $subscription_repo->generateLineItems($target, false, true);
|
||||
|
||||
$credit->line_items = array_merge($line_items, $this->calculateProRataRefundItems($last_invoice, $last_invoice_is_credit));
|
||||
$credit->discount = $last_invoice->discount;
|
||||
$credit->is_amount_discount = $last_invoice->is_amount_discount;
|
||||
|
||||
$credit->line_items = $this->calculateProRataRefundItems($last_invoice, true);
|
||||
|
||||
$data = [
|
||||
'client_id' => $last_invoice->client_id,
|
||||
@ -696,6 +810,7 @@ class SubscriptionService
|
||||
$invoice->subscription_id = $target->id;
|
||||
|
||||
$invoice->line_items = array_merge($subscription_repo->generateLineItems($target), $this->calculateProRataRefundItems($last_invoice));
|
||||
$invoice->is_proforma = true;
|
||||
|
||||
$data = [
|
||||
'client_id' => $client_id,
|
||||
@ -711,6 +826,40 @@ class SubscriptionService
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* When changing plans we need to generate a pro rata
|
||||
* invoice which takes into account any credits.
|
||||
*
|
||||
* @param Subscription $target
|
||||
* @return Invoice
|
||||
*/
|
||||
private function changePlanInvoice($target, $client_id)
|
||||
{
|
||||
$subscription_repo = new SubscriptionRepository();
|
||||
$invoice_repo = new InvoiceRepository();
|
||||
|
||||
$invoice = InvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
|
||||
$invoice->date = now()->format('Y-m-d');
|
||||
$invoice->subscription_id = $target->id;
|
||||
|
||||
$invoice->line_items = $subscription_repo->generateLineItems($target);
|
||||
$invoice->is_proforma = true;
|
||||
|
||||
$data = [
|
||||
'client_id' => $client_id,
|
||||
'quantity' => 1,
|
||||
'date' => now()->format('Y-m-d'),
|
||||
];
|
||||
|
||||
return $invoice_repo->save($data, $invoice)
|
||||
->service()
|
||||
->markSent()
|
||||
->fillDefaults()
|
||||
->save();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function createInvoiceV2($bundle, $client_id, $valid_coupon = false)
|
||||
{
|
||||
|
||||
@ -720,7 +869,8 @@ class SubscriptionService
|
||||
$invoice = InvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
|
||||
$invoice->subscription_id = $this->subscription->id;
|
||||
$invoice->client_id = $client_id;
|
||||
|
||||
$invoice->is_proforma = true;
|
||||
$invoice->number = ctrans('texts.subscription') . "_" . now()->format('Y-m-d') . "_" . rand(0,100000);
|
||||
$line_items = $bundle->map(function ($item){
|
||||
|
||||
$line_item = new InvoiceItem;
|
||||
@ -760,6 +910,7 @@ class SubscriptionService
|
||||
$invoice = InvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
|
||||
$invoice->line_items = $subscription_repo->generateLineItems($this->subscription);
|
||||
$invoice->subscription_id = $this->subscription->id;
|
||||
$invoice->is_proforman = true;
|
||||
|
||||
if(strlen($data['coupon']) >=1 && ($data['coupon'] == $this->subscription->promo_code) && $this->subscription->promo_discount > 0)
|
||||
{
|
||||
@ -771,7 +922,6 @@ class SubscriptionService
|
||||
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
}
|
||||
|
||||
|
||||
return $invoice_repo->save($data, $invoice);
|
||||
|
||||
}
|
||||
@ -860,14 +1010,11 @@ class SubscriptionService
|
||||
*/
|
||||
public function triggerWebhook($context)
|
||||
{
|
||||
nlog("trigger webhook");
|
||||
|
||||
if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) {
|
||||
return ["message" => "Success", "status_code" => 200];
|
||||
}
|
||||
|
||||
nlog("past first if");
|
||||
|
||||
$response = false;
|
||||
|
||||
$body = array_merge($context, [
|
||||
@ -876,8 +1023,6 @@ class SubscriptionService
|
||||
|
||||
$response = $this->sendLoad($this->subscription, $body);
|
||||
|
||||
nlog("after response");
|
||||
|
||||
/* Append the response to the system logger body */
|
||||
if(is_array($response)){
|
||||
|
||||
@ -1098,8 +1243,6 @@ class SubscriptionService
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return $this->handleRedirect('client/subscriptions');
|
||||
|
||||
}
|
||||
|
@ -193,6 +193,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'invoice_task_lock' => (bool) $company->invoice_task_lock,
|
||||
'convert_payment_currency' => (bool) $company->convert_payment_currency,
|
||||
'convert_expense_currency' => (bool) $company->convert_expense_currency,
|
||||
'notify_vendor_when_paid' => (bool) $company->notify_vendor_when_paid,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ use App\Models\Document;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
/**
|
||||
* class RecurringExpenseTransformer.
|
||||
@ -33,6 +34,8 @@ class RecurringExpenseTransformer extends EntityTransformer
|
||||
*/
|
||||
protected $availableIncludes = [
|
||||
'documents',
|
||||
'client',
|
||||
'vendor',
|
||||
];
|
||||
|
||||
public function includeDocuments(RecurringExpense $recurring_expense)
|
||||
@ -42,6 +45,28 @@ class RecurringExpenseTransformer extends EntityTransformer
|
||||
return $this->includeCollection($recurring_expense->documents, $transformer, Document::class);
|
||||
}
|
||||
|
||||
public function includeClient(RecurringExpense $recurring_expense): ?Item
|
||||
{
|
||||
$transformer = new ClientTransformer($this->serializer);
|
||||
|
||||
if (!$recurring_expense->client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->includeItem($recurring_expense->client, $transformer, Client::class);
|
||||
}
|
||||
|
||||
public function includeVendor(RecurringExpense $recurring_expense): ?Item
|
||||
{
|
||||
$transformer = new VendorTransformer($this->serializer);
|
||||
|
||||
if (!$recurring_expense->vendor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->includeItem($recurring_expense->vendor, $transformer, Vendor::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecurringExpense $recurring_expense
|
||||
*
|
||||
|
@ -129,12 +129,12 @@ class Helpers
|
||||
|
||||
if(!$string_hit)
|
||||
return $value;
|
||||
// 04-10-2022 Return Early if no reserved keywords are present, this is a very expensive process
|
||||
|
||||
// 04-10-2022 Return Early if no reserved keywords are present, this is a very expensive process
|
||||
Carbon::setLocale($entity->locale());
|
||||
|
||||
if (!$currentDateTime) {
|
||||
$currentDateTime = Carbon::now();
|
||||
$currentDateTime = Carbon::now()->timezone($entity->timezone()->name);
|
||||
}
|
||||
|
||||
$replacements = [
|
||||
|
@ -66,7 +66,8 @@ trait GeneratesCounter
|
||||
$counter = 1;
|
||||
}
|
||||
|
||||
$counter_entity = $client->group_settings;
|
||||
// $counter_entity = $client->group_settings;
|
||||
$counter_entity = $client->group_settings ?: $client->company;
|
||||
} else {
|
||||
$counter = $client->company->settings->{$counter_string};
|
||||
$counter_entity = $client->company;
|
||||
|
@ -25,7 +25,7 @@ class PDF extends FPDI
|
||||
$this->SetTextColor(135, 135, 135);
|
||||
|
||||
$trans = ctrans('texts.pdf_page_info', ['current' => $this->PageNo(), 'total' => '{nb}']);
|
||||
$trans = iconv('UTF-8', 'ISO-8859-7', $trans);
|
||||
// $trans = iconv('UTF-8', 'ISO-8859-7', $trans);
|
||||
$this->Cell(0, 5, $trans, 0, 0, $this->text_alignment);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ trait SettingsSaver
|
||||
continue;
|
||||
}
|
||||
/*Separate loop if it is a _id field which is an integer cast as a string*/
|
||||
elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || ($key == 'payment_terms' && strlen($settings->{$key}) >= 1) || ($key == 'valid_until' && property_exists($settings, $key) && strlen($settings->{$key}) >= 1)) {
|
||||
elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || ($key == 'payment_terms' && property_exists($settings, $key) && strlen($settings->{$key}) >= 1) || ($key == 'valid_until' && property_exists($settings, $key) && strlen($settings->{$key}) >= 1)) {
|
||||
$value = 'integer';
|
||||
|
||||
if($key == 'gmail_sending_user_id' || $key == 'besr_id')
|
||||
|
@ -12,6 +12,8 @@
|
||||
namespace App\Utils\Traits;
|
||||
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Psr7\Message;
|
||||
|
||||
/**
|
||||
* Class SubscriptionHooker.
|
||||
@ -34,10 +36,6 @@ trait SubscriptionHooker
|
||||
'headers' => $headers,
|
||||
]);
|
||||
|
||||
nlog('method name must be a string');
|
||||
nlog($subscription->webhook_configuration['post_purchase_rest_method']);
|
||||
nlog($subscription->webhook_configuration['post_purchase_url']);
|
||||
|
||||
$post_purchase_rest_method = (string) $subscription->webhook_configuration['post_purchase_rest_method'];
|
||||
$post_purchase_url = (string) $subscription->webhook_configuration['post_purchase_url'];
|
||||
|
||||
@ -47,7 +45,18 @@ trait SubscriptionHooker
|
||||
]);
|
||||
|
||||
return array_merge($body, json_decode($response->getBody(), true));
|
||||
} catch (\Exception $e) {
|
||||
} catch (ClientException $e) {
|
||||
|
||||
$message = $e->getMessage();
|
||||
|
||||
$error = json_decode($e->getResponse()->getBody()->getContents());
|
||||
|
||||
if(property_exists($error, 'message'))
|
||||
$message = $error->message;
|
||||
|
||||
return array_merge($body, ['message' => $message, 'status_code' => 500]);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return array_merge($body, ['message' => $e->getMessage(), 'status_code' => 500]);
|
||||
}
|
||||
}
|
||||
|
@ -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.49',
|
||||
'app_tag' => '5.5.49',
|
||||
'app_version' => '5.5.50',
|
||||
'app_tag' => '5.5.50',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
@ -190,7 +190,8 @@ return [
|
||||
'ninja_stripe_client_id' => env('NINJA_STRIPE_CLIENT_ID', null),
|
||||
'ninja_default_company_id' => env('NINJA_COMPANY_ID', null),
|
||||
'ninja_default_company_gateway_id' => env('NINJA_COMPANY_GATEWAY_ID', null),
|
||||
'ninja_hosted_secret' => env('NINJA_HOSTED_SECRET', null),
|
||||
'ninja_hosted_secret' => env('NINJA_HOSTED_SECRET', ''),
|
||||
'ninja_hosted_header' =>env('NINJA_HEADER',''),
|
||||
'internal_queue_enabled' => env('INTERNAL_QUEUE_ENABLED', true),
|
||||
'ninja_apple_api_key' => env('APPLE_API_KEY', false),
|
||||
'ninja_apple_private_key' => env('APPLE_PRIVATE_KEY', false),
|
||||
|
@ -29,14 +29,8 @@ class ProductFactory extends Factory
|
||||
'cost' => $this->faker->numberBetween(1, 1000),
|
||||
'price' => $this->faker->numberBetween(1, 1000),
|
||||
'quantity' => $this->faker->numberBetween(1, 100),
|
||||
// 'tax_name1' => 'GST',
|
||||
// 'tax_rate1' => 10,
|
||||
// 'tax_name2' => 'VAT',
|
||||
// 'tax_rate2' => 17.5,
|
||||
// 'tax_name3' => 'THIRDTAX',
|
||||
// 'tax_rate3' => 5,
|
||||
'custom_value1' => $this->faker->text(20),
|
||||
'custom_value2' => $this->faker->text(20),
|
||||
'custom_value1' => 'https://picsum.photos/200',
|
||||
'custom_value2' => rand(0,100),
|
||||
'custom_value3' => $this->faker->text(20),
|
||||
'custom_value4' => $this->faker->text(20),
|
||||
'is_deleted' => false,
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
Schema::table('companies', function (Blueprint $table)
|
||||
{
|
||||
$table->boolean('notify_vendor_when_paid')->default(false);
|
||||
});
|
||||
|
||||
Schema::table('invoices', function (Blueprint $table)
|
||||
{
|
||||
$table->boolean('is_proforma')->default(false);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -4301,7 +4301,7 @@ $LANG = array(
|
||||
'becs_mandate' => 'By providing your bank account details, you agree to this <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">Direct Debit Request and the Direct Debit Request service agreement</a>, and authorise Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 (“Stripe”) to debit your account through the Bulk Electronic Clearing System (BECS) on behalf of :company (the “Merchant”) for any amounts separately communicated to you by the Merchant. You certify that you are either an account holder or an authorised signatory on the account listed above.',
|
||||
'you_need_to_accept_the_terms_before_proceeding' => 'You need to accept the terms before proceeding.',
|
||||
'direct_debit' => 'Direct Debit',
|
||||
'clone_to_expense' => 'Clone to expense',
|
||||
'clone_to_expense' => 'Clone to Expense',
|
||||
'checkout' => 'Checkout',
|
||||
'acss' => 'Pre-authorized debit payments',
|
||||
'invalid_amount' => 'Invalid amount. Number/Decimal values only.',
|
||||
@ -4906,7 +4906,12 @@ $LANG = array(
|
||||
'backup_restore' => 'Backup | Restore',
|
||||
'export_company' => 'Create company backup',
|
||||
'backup' => 'Backup',
|
||||
|
||||
'notification_purchase_order_created_body' => 'The following purchase_order :purchase_order was created for vendor :vendor for :amount.',
|
||||
'notification_purchase_order_created_subject' => 'Purchase Order :purchase_order was created for :vendor',
|
||||
'notification_purchase_order_sent_subject' => 'Purchase Order :purchase_order was sent to :vendor',
|
||||
'notification_purchase_order_sent' => 'The following vendor :vendor was emailed Purchase Order :purchase_order for :amount.',
|
||||
'subscription_blocked' => 'This product is a restricted item, please contact the vendor for further information.',
|
||||
'subscription_blocked_title' => 'Product not available.',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -2,7 +2,7 @@
|
||||
@section('meta_title', ctrans('texts.purchase'))
|
||||
|
||||
@section('body')
|
||||
@livewire('billing-portal-purchase', ['subscription' => $subscription, 'company' => $subscription->company, 'contact' => auth()->guard('contact')->user(), 'hash' => $hash, 'request_data' => $request_data, 'campaign' => request()->query('campaign') ?? null])
|
||||
@livewire('billing-portal-purchase', ['subscription' => $subscription->id, 'db' => $subscription->company->db, 'hash' => $hash, 'request_data' => $request_data, 'campaign' => request()->query('campaign') ?? null])
|
||||
@stop
|
||||
|
||||
@push('footer')
|
||||
|
@ -2,7 +2,7 @@
|
||||
@section('meta_title', ctrans('texts.purchase'))
|
||||
|
||||
@section('body')
|
||||
@livewire('billing-portal-purchasev2', ['subscription' => $subscription, 'company' => $subscription->company, 'contact' => auth()->guard('contact')->user(), 'hash' => $hash, 'request_data' => $request_data, 'campaign' => request()->query('campaign') ?? null])
|
||||
@livewire('billing-portal-purchasev2', ['subscription' => $subscription->id, 'db' => $subscription->company->db, 'hash' => $hash, 'request_data' => $request_data, 'campaign' => request()->query('campaign') ?? null])
|
||||
@stop
|
||||
|
||||
@push('footer')
|
||||
|
@ -5,13 +5,21 @@
|
||||
|
||||
<div class="grid lg:grid-cols-12 py-8">
|
||||
<div class="col-span-12 lg:col-span-8 lg:col-start-3 xl:col-span-6 xl:col-start-4 px-6">
|
||||
@if($register_company->account && !$register_company->account->isPaid())
|
||||
<div class="flex justify-center">
|
||||
<img class="h-32 w-auto" src="{{ $register_company->present()->logo() }}" alt="{{ ctrans('texts.logo') }}">
|
||||
</div>
|
||||
<img src="{{ asset('images/invoiceninja-black-logo-2.png') }}"
|
||||
class="border-b border-gray-100 h-18 pb-4" alt="Invoice Ninja logo">
|
||||
</div>
|
||||
@elseif(isset($register_company) && !is_null($register_company))
|
||||
<div class="flex justify-center">
|
||||
<img src="{{ $register_company->present()->logo() }}"
|
||||
class="mx-auto border-b border-gray-100 h-18 pb-4" alt="{{ $register_company->present()->name() }} logo">
|
||||
</div>
|
||||
@endif
|
||||
<h1 class="text-center text-3xl mt-8">{{ ctrans('texts.register') }}</h1>
|
||||
<p class="block text-center text-gray-600">{{ ctrans('texts.register_label') }}</p>
|
||||
|
||||
<form action="{{ route('client.register', request()->route('company_key')) }}" method="POST" x-data="{more: false, busy: false, isSubmitted: false}" x-on:submit="isSubmitted = true">
|
||||
<form id="register-form" action="{{ route('client.register', request()->route('company_key')) }}" method="POST" x-data="{more: false, busy: false, isSubmitted: false}" x-on:submit="isSubmitted = true">
|
||||
@if($register_company)
|
||||
<input type="hidden" name="company_key" value="{{ $register_company->company_key }}">
|
||||
@endif
|
||||
@ -54,6 +62,18 @@
|
||||
type="password"
|
||||
name="{{ $field['key'] }}"
|
||||
/>
|
||||
@elseif($field['key'] === 'currency_id')
|
||||
<select
|
||||
id="currency_id"
|
||||
class="input w-full form-select bg-white"
|
||||
name="currency_id">
|
||||
@foreach(App\Utils\TranslationHelper::getCurrencies() as $currency)
|
||||
<option
|
||||
{{ $currency->id == $register_company->settings->currency_id ? 'selected' : null }} value="{{ $currency->id }}">
|
||||
{{ $currency->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@elseif($field['key'] === 'country_id')
|
||||
<select
|
||||
id="shipping_country"
|
||||
@ -112,6 +132,9 @@
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center mt-8">
|
||||
|
||||
<a href="{{route('client.login')}}" class="button button-info bg-green-600 text-white">{{ ctrans('texts.login_label') }}</a>
|
||||
|
||||
<span class="inline-flex items-center" x-data="{ terms_of_service: false, privacy_policy: false }">
|
||||
@if(!empty($register_company->settings->client_portal_terms) || !empty($register_company->settings->client_portal_privacy_policy))
|
||||
<input type="checkbox" name="terms" class="form-checkbox mr-2 cursor-pointer" checked>
|
||||
@ -128,8 +151,10 @@
|
||||
@enderror
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<button class="button button-primary bg-blue-600" :disabled={{ $submitsForm == 'true' ? 'isSubmitted' : 'busy'}} x-on:click="busy = true">{{ ctrans('texts.register')}}</button>
|
||||
|
||||
<button class="button button-primary bg-blue-600" :disabled={{ $submitsForm == 'true' ? 'isSubmitted' : 'busy'}} x-on:click="busy = true">
|
||||
{{ ctrans('texts.register')}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="px-4 py-5 sm:px-6 lg:grid lg:grid-cols-3 lg:gap-4 lg:flex lg:items-center">
|
||||
<div class="px-4 py-2 sm:px-6 lg:grid lg:grid-cols-3 lg:gap-4 lg:flex lg:items-center">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 mr-4">
|
||||
{{ $title }}
|
||||
</dt>
|
||||
|
@ -196,6 +196,13 @@
|
||||
</li>
|
||||
@endforeach
|
||||
@endif
|
||||
@if(auth()->guard('contact')->check())
|
||||
<li class="flex py-6">
|
||||
<div class="flex w-full text-left mt-8">
|
||||
<a href="{{route('client.dashboard')}}" class="button-link text-primary">{{ ctrans('texts.go_back') }}</a>
|
||||
</div>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -209,7 +216,7 @@
|
||||
|
||||
@foreach($bundle->toArray() as $item)
|
||||
<div class="flex justify-between mt-1 mb-1">
|
||||
<span class="font-light text-sm uppercase">{{$item['product']}} x {{$item['qty']}}</span>
|
||||
<span class="font-light text-sm">{{ $item['qty'] }} x {{ substr(str_replace(["\r","\n","<BR>","<BR />","<br>","<br />"]," ", $item['product']), 0, 30) . "..." }}</span>
|
||||
<span class="font-bold text-sm">{{ $item['price'] }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
@ -284,6 +291,7 @@
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@if($is_eligible)
|
||||
<div class="mt-4 container mx-auto flex w-full justify-center" x-show.important="toggle" x-transition>
|
||||
<span class="">
|
||||
<svg class="animate-spin h-8 w-8 text-primary mx-auto justify-center w-full" xmlns="http://www.w3.org/2000/svg"
|
||||
@ -295,6 +303,9 @@
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
@else
|
||||
<small class="mt-4 block">{{ $this->not_eligible_message }}</small>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -13,6 +13,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('credits-table', ['company' => $company])
|
||||
@livewire('credits-table', ['company_id' => $company->id, 'db' => $company->db])
|
||||
</div>
|
||||
@endsection
|
@ -34,7 +34,6 @@
|
||||
@include('portal.ninja2020.components.entity-documents', ['entity' => $credit])
|
||||
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $credit, 'invitation' => $invitation])
|
||||
|
||||
|
||||
@endsection
|
||||
|
||||
@ -45,18 +44,5 @@
|
||||
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
|
||||
// clipboard.on('success', function(e) {
|
||||
// console.info('Action:', e.action);
|
||||
// console.info('Text:', e.text);
|
||||
// console.info('Trigger:', e.trigger);
|
||||
|
||||
// e.clearSelection();
|
||||
// });
|
||||
|
||||
// clipboard.on('error', function(e) {
|
||||
// console.error('Action:', e.action);
|
||||
// console.error('Trigger:', e.trigger);
|
||||
// });
|
||||
|
||||
</script>
|
||||
@endsection
|
||||
|
@ -14,5 +14,5 @@
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
@livewire('documents-table', ['client' => $client, 'company' => $company])
|
||||
@livewire('documents-table', ['client_id' => $client->id, 'db' => $company->db])
|
||||
@endsection
|
||||
|
@ -5,6 +5,7 @@
|
||||
<form action="{{route('client.payments.credit_response')}}" method="post" id="credit-payment">
|
||||
@csrf
|
||||
<input type="hidden" name="payment_hash" value="{{$payment_hash}}">
|
||||
<input type="hidden" name="hash" value="{{ request()->query('hash')}}">
|
||||
</form>
|
||||
|
||||
<div class="container mx-auto">
|
||||
|
@ -0,0 +1,31 @@
|
||||
@extends('portal.ninja2020.layout.clean')
|
||||
@section('meta_title', ctrans('texts.error'))
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="flex h-screen">
|
||||
<div class="m-auto md:w-1/2 lg:w-1/2">
|
||||
<div class="flex flex-col items-center">
|
||||
|
||||
@if($account && !$account->isPaid())
|
||||
<div>
|
||||
<img src="{{ asset('images/invoiceninja-black-logo-2.png') }}"
|
||||
class="border-b border-gray-100 h-18 pb-4" alt="Invoice Ninja logo">
|
||||
</div>
|
||||
@elseif(isset($company) && !is_null($company))
|
||||
<div>
|
||||
<img src="{{ $company->present()->logo() }}"
|
||||
class="mx-auto border-b border-gray-100 h-18 pb-4" alt="{{ $company->present()->name() }} logo">
|
||||
</div>
|
||||
@endif
|
||||
<h1 class="text-center text-3xl mt-10">{{ ctrans("texts.subscription_blocked_title") }}</h1>
|
||||
<p class="text-center opacity-75 mt-10">{{ ctrans('texts.subscription_blocked') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@stop
|
||||
|
||||
@push('footer')
|
||||
|
||||
@endpush
|
@ -23,6 +23,6 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex flex-col mt-4">
|
||||
@livewire('invoices-table', ['company' => $company])
|
||||
@livewire('invoices-table', ['company_id' => $company->id, 'db' => $company->db])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -31,7 +31,7 @@
|
||||
<input type="hidden" name="company_gateway_id" id="company_gateway_id">
|
||||
<input type="hidden" name="payment_method_id" id="payment_method_id">
|
||||
<input type="hidden" name="signature">
|
||||
|
||||
<input type="hidden" name="hash" value="{{ $hash }}">
|
||||
<input type="hidden" name="payable_invoices[0][amount]" value="{{ $invoice->partial > 0 ? \App\Utils\Number::formatValue($invoice->partial, $invoice->client->currency()) : \App\Utils\Number::formatValue($invoice->balance, $invoice->client->currency()) }}">
|
||||
<input type="hidden" name="payable_invoices[0][invoice_id]" value="{{ $invoice->hashed_id }}">
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('payment-methods-table', ['client' => $client, 'company' => $company])
|
||||
@livewire('payment-methods-table', ['client_id' => $client->id, 'db' => $company->db])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('payments-table', ['company' => $company])
|
||||
@livewire('payments-table', ['company_id' => $company->id, 'db' => $company->db])
|
||||
</div>
|
||||
@endsection
|
@ -26,6 +26,6 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col mt-4">
|
||||
@livewire('quotes-table', ['company' => $company])
|
||||
@livewire('quotes-table', ['company_id' => $company->id, 'db' => $company->db])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -54,7 +54,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'domain_db','check_clie
|
||||
Route::post('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'bulk'])->name('invoices.bulk');
|
||||
Route::get('invoices/payment', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'catch_bulk'])->name('invoices.catch_bulk');
|
||||
Route::post('invoices/download', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'download'])->name('invoices.download');
|
||||
Route::get('invoices/{invoice}', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'show'])->name('invoice.show');
|
||||
Route::get('invoices/{invoice}/{hash?}', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'show'])->name('invoice.show');
|
||||
Route::get('invoices/{invoice_invitation}', [App\Http\Controllers\ClientPortal\InvoiceController::class, 'show'])->name('invoice.show_invitation');
|
||||
|
||||
Route::get('recurring_invoices', [App\Http\Controllers\ClientPortal\RecurringInvoiceController::class, 'index'])->name('recurring_invoices.index')->middleware('portal_enabled');
|
||||
|
@ -100,7 +100,7 @@ class CreditsTest extends TestCase
|
||||
$c2->load('client');
|
||||
$c3->load('client');
|
||||
|
||||
Livewire::test(CreditsTable::class, ['company' => $company])
|
||||
Livewire::test(CreditsTable::class, ['company_id' => $company->id, 'db' => $company->db])
|
||||
->assertDontSee('testing-number-01')
|
||||
->assertSee('testing-number-02')
|
||||
->assertSee('testing-number-03');
|
||||
@ -167,7 +167,7 @@ class CreditsTest extends TestCase
|
||||
|
||||
$this->actingAs($client->contacts->first(), 'contact');
|
||||
|
||||
Livewire::test(CreditsTable::class, ['company' => $company])
|
||||
Livewire::test(CreditsTable::class, ['company_id' => $company->id, 'db' => $company->db])
|
||||
->assertSee('testing-number-01')
|
||||
->assertSee('testing-number-02')
|
||||
->assertSee('testing-number-03');
|
||||
|
@ -86,12 +86,12 @@ class InvoicesTest extends TestCase
|
||||
|
||||
$this->actingAs($client->contacts->first(), 'contact');
|
||||
|
||||
Livewire::test(InvoicesTable::class, ['company' => $company])
|
||||
Livewire::test(InvoicesTable::class, ['company_id' => $company->id, 'db' => $company->db])
|
||||
->assertSee($sent->number)
|
||||
->assertSee($paid->number)
|
||||
->assertSee($unpaid->number);
|
||||
|
||||
Livewire::test(InvoicesTable::class, ['company' => $company])
|
||||
Livewire::test(InvoicesTable::class, ['company_id' => $company->id, 'db' => $company->db])
|
||||
->set('status', ['paid'])
|
||||
->assertSee($paid->number)
|
||||
->assertDontSee($unpaid->number);
|
||||
|
@ -144,7 +144,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'balance' => 11,
|
||||
'status_id' => 2,
|
||||
'total_taxes' => 1,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'terms' => 'nada',
|
||||
'discount' => 0,
|
||||
'tax_rate1' => 0,
|
||||
@ -183,7 +183,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'balance' => 10,
|
||||
'status_id' => 2,
|
||||
'total_taxes' => 1,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'terms' => 'nada',
|
||||
'discount' => 0,
|
||||
'tax_rate1' => 10,
|
||||
@ -226,7 +226,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'balance' => 10,
|
||||
'status_id' => 2,
|
||||
'total_taxes' => 1,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'terms' => 'nada',
|
||||
'discount' => 0,
|
||||
'tax_rate1' => 10,
|
||||
@ -282,7 +282,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'balance' => 10,
|
||||
'status_id' => 2,
|
||||
'total_taxes' => 0,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'terms' => 'nada',
|
||||
'discount' => 0,
|
||||
'tax_rate1' => 0,
|
||||
@ -313,7 +313,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'amount' => 10,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
]);
|
||||
|
||||
$pl = new ProfitLoss($this->company, $this->payload);
|
||||
@ -334,7 +334,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
|
||||
$e = ExpenseFactory::create($this->company->id, $this->user->id);
|
||||
$e->amount = 10;
|
||||
$e->date = '2022-01-01';
|
||||
$e->date = now()->format('Y-m-d');
|
||||
$e->calculate_tax_by_amount = true;
|
||||
$e->tax_amount1 = 10;
|
||||
$e->save();
|
||||
@ -358,7 +358,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
|
||||
$e = ExpenseFactory::create($this->company->id, $this->user->id);
|
||||
$e->amount = 10;
|
||||
$e->date = '2022-01-01';
|
||||
$e->date = now()->format('Y-m-d');
|
||||
$e->tax_rate1 = 10;
|
||||
$e->tax_name1 = 'GST';
|
||||
$e->uses_inclusive_taxes = false;
|
||||
@ -383,7 +383,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
|
||||
$e = ExpenseFactory::create($this->company->id, $this->user->id);
|
||||
$e->amount = 10;
|
||||
$e->date = '2022-01-01';
|
||||
$e->date = now()->format('Y-m-d');
|
||||
$e->tax_rate1 = 10;
|
||||
$e->tax_name1 = 'GST';
|
||||
$e->uses_inclusive_taxes = false;
|
||||
@ -410,7 +410,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'amount' => 10,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'exchange_rate' => 1,
|
||||
'currency_id' => $this->company->settings->currency_id,
|
||||
]);
|
||||
@ -440,7 +440,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'amount' => 10,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'exchange_rate' => 1,
|
||||
'currency_id' => $this->company->settings->currency_id,
|
||||
]);
|
||||
@ -454,7 +454,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'amount' => 10,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'exchange_rate' => 1,
|
||||
'currency_id' => $this->company->settings->currency_id,
|
||||
]);
|
||||
@ -489,7 +489,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'balance' => 10,
|
||||
'status_id' => 2,
|
||||
'total_taxes' => 1,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'terms' => 'nada',
|
||||
'discount' => 0,
|
||||
'tax_rate1' => 10,
|
||||
@ -510,7 +510,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'amount' => 10,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'exchange_rate' => 1,
|
||||
'currency_id' => $this->company->settings->currency_id,
|
||||
]);
|
||||
@ -524,7 +524,7 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'amount' => 10,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'date' => '2022-01-01',
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'exchange_rate' => 1,
|
||||
'currency_id' => $this->company->settings->currency_id,
|
||||
]);
|
||||
|
@ -70,4 +70,14 @@ class DatesTest extends TestCase
|
||||
|
||||
$this->assertFalse($date_in_future->gt(Carbon::parse($date_in_past)->addDays(14)));
|
||||
}
|
||||
|
||||
/*Test time travelling behaves as expected */
|
||||
// public function testTimezoneShifts()
|
||||
// {
|
||||
// $this->travel(Carbon::parse('2022-12-20'));
|
||||
|
||||
// $this->assertEquals('2022-12-20', now()->setTimeZone('Pacific/Midway')->format('Y-m-d'));
|
||||
|
||||
// $this->travelBack();
|
||||
// }
|
||||
}
|
||||
|
@ -115,8 +115,8 @@ class GeneratesConvertedQuoteCounterTest extends TestCase
|
||||
|
||||
$this->assertNotNull($invoice);
|
||||
|
||||
$this->assertEquals('2022-Q0001', $quote->number);
|
||||
$this->assertEquals('2022-I0001', $invoice->number);
|
||||
$this->assertEquals(now()->format('Y'). '-Q0001', $quote->number);
|
||||
$this->assertEquals(now()->format('Y'). '-I0001', $invoice->number);
|
||||
|
||||
$settings = $this->client->getMergedSettings();
|
||||
$settings->invoice_number_counter = 100;
|
||||
|
Loading…
x
Reference in New Issue
Block a user