1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 21:22:58 +01:00

Merge pull request #4764 from turbo124/v5-stable

5.0.54
This commit is contained in:
David Bomba 2021-01-26 17:16:01 +11:00 committed by GitHub
commit 7d7005e013
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
132 changed files with 11199 additions and 10799 deletions

View File

@ -1 +1 @@
5.0.53 5.0.54

View File

@ -223,7 +223,7 @@ class CreateSingleAccount extends Command
'company_id' => $company->id, 'company_id' => $company->id,
]); ]);
$client->id_number = $this->getNextClientNumber($client); $client->number = $this->getNextClientNumber($client);
$settings = $client->settings; $settings = $client->settings;
$settings->currency_id = "1"; $settings->currency_id = "1";

View File

@ -400,7 +400,7 @@ class CreateTestData extends Command
'company_id' => $company->id, 'company_id' => $company->id,
]); ]);
$client->id_number = $this->getNextClientNumber($client); $client->number = $this->getNextClientNumber($client);
$settings = $client->settings; $settings = $client->settings;
$settings->currency_id = (string) rand(1, 79); $settings->currency_id = (string) rand(1, 79);

View File

@ -286,7 +286,7 @@ class DemoMode extends Command
'company_id' => $company->id, 'company_id' => $company->id,
]); ]);
$client->id_number = $this->getNextClientNumber($client); $client->number = $this->getNextClientNumber($client);
$settings = $client->settings; $settings = $client->settings;
$settings->currency_id = (string) rand(1, 3); $settings->currency_id = (string) rand(1, 3);
@ -307,6 +307,14 @@ class DemoMode extends Command
'client_id' => $client->id, 'client_id' => $client->id,
'company_id' => $client->company_id, 'company_id' => $client->company_id,
]); ]);
Expense::all()->each(function ($expense){
$expense->number = $this->getNextExpenseNumber($expense);
$expense->save();
});
} }
private function createVendor($client, $assigned_user_id = null) private function createVendor($client, $assigned_user_id = null)
@ -329,15 +337,23 @@ class DemoMode extends Command
'company_id' => $client->company_id, 'company_id' => $client->company_id,
'is_primary' => 0, 'is_primary' => 0,
]); ]);
$vendor->id_number = $this->getNextVendorNumber($vendor);
$vendor->save();
} }
private function createTask($client, $assigned_user_id = null) private function createTask($client, $assigned_user_id = null)
{ {
$vendor = Task::factory()->create([ $task = Task::factory()->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'company_id' => $client->company_id, 'company_id' => $client->company_id,
'client_id' => $client->id 'client_id' => $client->id
]); ]);
$task->number = $this->getNextTaskNumber($task);
$task->save();
} }
private function createProject($client, $assigned_user_id = null) private function createProject($client, $assigned_user_id = null)

View File

@ -13,6 +13,7 @@ namespace App\Events\Invoice;
use App\Models\Company; use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
/** /**
@ -27,6 +28,8 @@ class InvoiceWasPaid
*/ */
public $invoice; public $invoice;
public $payment;
public $company; public $company;
public $event_vars; public $event_vars;
@ -38,9 +41,10 @@ class InvoiceWasPaid
* @param Company $company * @param Company $company
* @param array $event_vars * @param array $event_vars
*/ */
public function __construct(Invoice $invoice, Company $company, array $event_vars) public function __construct(Invoice $invoice, Payment $payment, Company $company, array $event_vars)
{ {
$this->invoice = $invoice; $this->invoice = $invoice;
$this->payment = $payment;
$this->company = $company; $this->company = $company;
$this->event_vars = $event_vars; $this->event_vars = $event_vars;
} }

View File

@ -44,6 +44,7 @@ class InvoiceFactory
$invoice->custom_value4 = 0; $invoice->custom_value4 = 0;
$invoice->amount = 0; $invoice->amount = 0;
$invoice->balance = 0; $invoice->balance = 0;
$invoice->paid_to_date = 0;
$invoice->partial = 0; $invoice->partial = 0;
$invoice->user_id = $user_id; $invoice->user_id = $user_id;
$invoice->company_id = $company_id; $invoice->company_id = $company_id;

View File

@ -50,6 +50,7 @@ class InvoiceToRecurringInvoiceFactory
$recurring_invoice->last_sent_date = null; $recurring_invoice->last_sent_date = null;
$recurring_invoice->next_send_date = null; $recurring_invoice->next_send_date = null;
$recurring_invoice->remaining_cycles = 0; $recurring_invoice->remaining_cycles = 0;
$recurring_invoice->paid_to_date = 0;
return $recurring_invoice; return $recurring_invoice;
} }

View File

@ -45,6 +45,7 @@ class QuoteFactory
$quote->partial = 0; $quote->partial = 0;
$quote->user_id = $user_id; $quote->user_id = $user_id;
$quote->company_id = $company_id; $quote->company_id = $company_id;
$quote->paid_to_date = 0;
return $quote; return $quote;
} }

View File

@ -49,6 +49,7 @@ class RecurringInvoiceFactory
$invoice->last_sent_date = null; $invoice->last_sent_date = null;
$invoice->next_send_date = null; $invoice->next_send_date = null;
$invoice->remaining_cycles = 0; $invoice->remaining_cycles = 0;
$invoice->paid_to_date = 0;
return $invoice; return $invoice;
} }

View File

@ -50,6 +50,7 @@ class RecurringInvoiceToInvoiceFactory
$invoice->recurring_id = $recurring_invoice->id; $invoice->recurring_id = $recurring_invoice->id;
$invoice->client_id = $client->id; $invoice->client_id = $client->id;
$invoice->auto_bill_enabled = $recurring_invoice->auto_bill_enabled; $invoice->auto_bill_enabled = $recurring_invoice->auto_bill_enabled;
$invoice->paid_to_date = 0;
return $invoice; return $invoice;
} }

View File

@ -136,7 +136,7 @@ class ActivityController extends BaseController
$backup = $activity->backup; $backup = $activity->backup;
if (! $backup || ! $backup->html_backup) { if (! $backup || ! $backup->html_backup) {
return response()->json(['message'=> 'No backup exists for this activity', 'errors' => new stdClass], 404); return response()->json(['message'=> ctrans('texts.no_backup_exists'), 'errors' => new stdClass], 404);
} }
$pdf = $this->makePdf(null, null, $backup->html_backup); $pdf = $this->makePdf(null, null, $backup->html_backup);

View File

@ -150,7 +150,7 @@ class BaseController extends Controller
*/ */
public function notFound() public function notFound()
{ {
return response()->json(['message' => '404 | Nothing to see here!'], 404) return response()->json(['message' => ctrans('texts.api_404')], 404)
->header('X-API-VERSION', config('ninja.minimum_client_version')) ->header('X-API-VERSION', config('ninja.minimum_client_version'))
->header('X-APP-VERSION', config('ninja.app_version')); ->header('X-APP-VERSION', config('ninja.app_version'));
} }
@ -198,7 +198,7 @@ class BaseController extends Controller
$updated_at = request()->has('updated_at') ? request()->input('updated_at') : 0; $updated_at = request()->has('updated_at') ? request()->input('updated_at') : 0;
if (auth()->user()->getCompany()->is_large && ! request()->has('updated_at')) { if (auth()->user()->getCompany()->is_large && ! request()->has('updated_at')) {
return response()->json(['message' => 'Cannot load a large account without a updated_at parameter', 'errors' =>[]], 401); return response()->json(['message' => ctrans('texts.large_account_update_parameter'), 'errors' =>[]], 401);
} }
$updated_at = date('Y-m-d H:i:s', $updated_at); $updated_at = date('Y-m-d H:i:s', $updated_at);

View File

@ -78,7 +78,7 @@ class PaymentController extends Controller
public function process(Request $request) public function process(Request $request)
{ {
$is_credit_payment = false; $is_credit_payment = false;
$token = false; $tokens = [];
if ($request->input('company_gateway_id') == CompanyGateway::GATEWAY_CREDIT) { if ($request->input('company_gateway_id') == CompanyGateway::GATEWAY_CREDIT) {
$is_credit_payment = true; $is_credit_payment = true;
@ -115,8 +115,7 @@ class PaymentController extends Controller
/* This loop checks for under / over payments and returns the user if a check fails */ /* This loop checks for under / over payments and returns the user if a check fails */
foreach($payable_invoices as $payable_invoice) foreach ($payable_invoices as $payable_invoice) {
{
/*Match the payable invoice to the Model Invoice*/ /*Match the payable invoice to the Model Invoice*/
@ -173,8 +172,7 @@ class PaymentController extends Controller
//$payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) { //$payable_invoices = $payable_invoices->map(function ($payable_invoice) use ($invoices, $settings) {
$payable_invoice_collection = collect(); $payable_invoice_collection = collect();
foreach($payable_invoices as $payable_invoice) foreach ($payable_invoices as $payable_invoice) {
{
// nlog($payable_invoice); // nlog($payable_invoice);
$payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']); $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']);
@ -229,12 +227,15 @@ class PaymentController extends Controller
$fee_totals = $first_invoice->amount - $starting_invoice_amount; $fee_totals = $first_invoice->amount - $starting_invoice_amount;
if ($gateway) { if ($gateway) {
$token = auth()->user()->client->gateway_token($gateway->id, $payment_method_id); $tokens = auth()->user()->client->gateway_tokens()
->whereCompanyGatewayId($gateway->id)
->whereGatewayTypeId($payment_method_id)
->get();
} }
$payment_hash = new PaymentHash; $payment_hash = new PaymentHash;
$payment_hash->hash = Str::random(128); $payment_hash->hash = Str::random(128);
$payment_hash->data = ['invoices' => $payable_invoices->toArray() , 'credits' => $credit_totals]; $payment_hash->data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals];
$payment_hash->fee_total = $fee_totals; $payment_hash->fee_total = $fee_totals;
$payment_hash->fee_invoice_id = $first_invoice->id; $payment_hash->fee_invoice_id = $first_invoice->id;
$payment_hash->save(); $payment_hash->save();
@ -250,7 +251,7 @@ class PaymentController extends Controller
'payment_hash' => $payment_hash->hash, 'payment_hash' => $payment_hash->hash,
'total' => $totals, 'total' => $totals,
'invoices' => $payable_invoices, 'invoices' => $payable_invoices,
'token' => $token, 'tokens' => $tokens,
'payment_method_id' => $payment_method_id, 'payment_method_id' => $payment_method_id,
'amount_with_fee' => $invoice_totals + $fee_totals, 'amount_with_fee' => $invoice_totals + $fee_totals,
]; ];
@ -266,7 +267,7 @@ class PaymentController extends Controller
->setPaymentHash($payment_hash) ->setPaymentHash($payment_hash)
->checkRequirements() ->checkRequirements()
->processPaymentView($data); ->processPaymentView($data);
} catch(\Exception $e) { } catch (\Exception $e) {
SystemLogger::dispatch( SystemLogger::dispatch(
$e->getMessage(), $e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::CATEGORY_GATEWAY_RESPONSE,
@ -292,7 +293,7 @@ class PaymentController extends Controller
->setPaymentHash($payment_hash) ->setPaymentHash($payment_hash)
->checkRequirements() ->checkRequirements()
->processPaymentResponse($request); ->processPaymentResponse($request);
} catch(\Exception $e) { } catch (\Exception $e) {
SystemLogger::dispatch( SystemLogger::dispatch(
$e->getMessage(), $e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::CATEGORY_GATEWAY_RESPONSE,
@ -300,15 +301,13 @@ class PaymentController extends Controller
SystemLog::TYPE_FAILURE, SystemLog::TYPE_FAILURE,
auth('contact')->user()->client auth('contact')->user()->client
); );
throw new PaymentFailed($e->getMessage());
} }
} }
/** /**
* Pay for invoice/s using credits only. * Pay for invoice/s using credits only.
* *
* @param Request $request The request object * @param Request $request The request object
* @return Response The response view * @return Response The response view
*/ */
public function credit_response(Request $request) public function credit_response(Request $request)

View File

@ -151,7 +151,8 @@ class PaymentMethodController extends Controller
event(new MethodDeleted($payment_method, auth('contact')->user()->company, Ninja::eventVars())); event(new MethodDeleted($payment_method, auth('contact')->user()->company, Ninja::eventVars()));
$payment_method->delete(); $payment_method->delete();
} catch (Exception $e) { } catch (Exception $e) {
Log::error(json_encode($e));
nlog($e->getMessage());
return back(); return back();
} }

View File

@ -501,6 +501,6 @@ class CompanyController extends BaseController
} }
} }
return response()->json(['message' => 'success'], 200); return response()->json(['message' => ctrans('texts.success')], 200);
} }
} }

View File

@ -115,7 +115,7 @@ class CompanyUserController extends BaseController
$company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first(); $company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first();
if (! $company_user) { if (! $company_user) {
throw new ModelNotFoundException('Company User record not found'); throw new ModelNotFoundException(ctrans('texts.company_user_not_found'));
return; return;
} }

View File

@ -484,7 +484,7 @@ class CreditController extends BaseController
$credits = Credit::withTrashed()->whereIn('id', $this->transformKeys($ids)); $credits = Credit::withTrashed()->whereIn('id', $this->transformKeys($ids));
if (! $credits) { if (! $credits) {
return response()->json(['message' => 'No Credits Found']); return response()->json(['message' => ctrans('texts.no_credits_found')]);
} }
$credits->each(function ($credit, $key) use ($action) { $credits->each(function ($credit, $key) use ($action) {
@ -561,7 +561,7 @@ class CreditController extends BaseController
break; break;
default: default:
return response()->json(['message' => "The requested action `{$action}` is not available."], 400); return response()->json(['message' => ctrans('texts.action_unavailable', ['action' => $action])], 400);
break; break;
} }
} }

View File

@ -124,7 +124,7 @@ class DocumentController extends BaseController
{ {
$this->document_repo->delete($document); $this->document_repo->delete($document);
return response()->json(['message'=>'success']); return response()->json(['message'=> ctrans('texts.success')]);
} }
public function bulk() public function bulk()
@ -136,7 +136,7 @@ class DocumentController extends BaseController
$documents = Document::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); $documents = Document::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $invoices) { if (! $invoices) {
return response()->json(['message' => 'No Documents Found']); return response()->json(['message' => ctrans('texts.no_documents_found')]);
} }
/* /*

View File

@ -481,7 +481,7 @@ class GroupSettingController extends BaseController
$group_settings = GroupSetting::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); $group_settings = GroupSetting::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $group_settings) { if (! $group_settings) {
return response()->json(['message' => 'No Group Settings Found']); return response()->json(['message' => ctrans('texts.no_group_settings_found')]);
} }
/* /*

View File

@ -95,7 +95,7 @@ class ImportController extends Controller
{ {
CSVImport::dispatch($request->all(), auth()->user()->company()); CSVImport::dispatch($request->all(), auth()->user()->company());
return response()->json(['message' => 'Importing data, email will be sent on completion'], 200); return response()->json(['message' => ctrans('texts.import_started')], 200);
} }
private function getEntityMap($entity_type) private function getEntityMap($entity_type)

View File

@ -387,7 +387,7 @@ class InvoiceController extends BaseController
} }
if ($invoice->isLocked()) { if ($invoice->isLocked()) {
return response()->json(['message' => 'Invoice is locked, no modifications allowed']); return response()->json(['message' => ctrans('texts.locked_invoice')]);
} }
$invoice = $this->invoice_repo->save($request->all(), $invoice); $invoice = $this->invoice_repo->save($request->all(), $invoice);
@ -526,13 +526,13 @@ class InvoiceController extends BaseController
if ($action == 'download' && $invoices->count() > 1) { if ($action == 'download' && $invoices->count() > 1) {
$invoices->each(function ($invoice) { $invoices->each(function ($invoice) {
if (auth()->user()->cannot('view', $invoice)) { if (auth()->user()->cannot('view', $invoice)) {
return response()->json(['message' => 'Insufficient privileges to access invoice '.$invoice->number]); return response()->json(['message' => ctrans('text.access_denied')]);
} }
}); });
ZipInvoices::dispatch($invoices, $invoices->first()->company, auth()->user()->email); ZipInvoices::dispatch($invoices, $invoices->first()->company, auth()->user()->email);
return response()->json(['message' => 'Email Sent!'], 200); return response()->json(['message' => ctrans('texts.sent_message')], 200);
} }
/* /*
@ -649,7 +649,7 @@ class InvoiceController extends BaseController
break; break;
case 'mark_paid': case 'mark_paid':
if ($invoice->balance < 0 || $invoice->status_id == Invoice::STATUS_PAID || $invoice->is_deleted === true) { if ($invoice->balance < 0 || $invoice->status_id == Invoice::STATUS_PAID || $invoice->is_deleted === true) {
return $this->errorResponse(['message' => 'Invoice cannot be marked as paid'], 400); return $this->errorResponse(['message' => ctrans('texts.invoice_cannot_be_marked_paid')], 400);
} }
$invoice = $invoice->service()->markPaid(); $invoice = $invoice->service()->markPaid();
@ -686,9 +686,7 @@ class InvoiceController extends BaseController
} }
break; break;
case 'delete': case 'delete':
//need to make sure the invoice is cancelled first!!
//$invoice->service()->handleCancellation()s->save();
nlog("inside delete");
$this->invoice_repo->delete($invoice); $this->invoice_repo->delete($invoice);
if (! $bulk) { if (! $bulk) {
@ -735,7 +733,7 @@ nlog("inside delete");
break; break;
default: default:
return response()->json(['message' => "The requested action `{$action}` is not available."], 400); return response()->json(['message' => ctrans('texts.action_unavailable', ['action' => $action])], 400);
break; break;
} }
} }

View File

@ -134,7 +134,7 @@ class LicenseController extends BaseController
} }
$error = [ $error = [
'message' => 'Invalid license, or invalid environment '.config('ninja.environment'), 'message' => ctrans('texts.invoice_license_or_environment', ['environment' => config('ninja.environment')]),
'errors' => new stdClass, 'errors' => new stdClass,
]; ];

View File

@ -33,6 +33,7 @@
* @OA\Property(property="custom_value4", type="string", example="", description="________"), * @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="vat_number", type="string", example="", description="________"), * @OA\Property(property="vat_number", type="string", example="", description="________"),
* @OA\Property(property="id_number", type="string", example="", description="________"), * @OA\Property(property="id_number", type="string", example="", description="________"),
* @OA\Property(property="number", type="string", example="", description="________"),
* @OA\Property(property="shipping_address1", type="string", example="", description="________"), * @OA\Property(property="shipping_address1", type="string", example="", description="________"),
* @OA\Property(property="shipping_address2", type="string", example="", description="________"), * @OA\Property(property="shipping_address2", type="string", example="", description="________"),
* @OA\Property(property="shipping_city", type="string", example="", description="________"), * @OA\Property(property="shipping_city", type="string", example="", description="________"),

View File

@ -30,6 +30,7 @@
* @OA\Property(property="line_items", type="object", example="", description="_________"), * @OA\Property(property="line_items", type="object", example="", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"), * @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),

View File

@ -29,6 +29,7 @@
* @OA\Property(property="line_items", type="object", example="", description="_________"), * @OA\Property(property="line_items", type="object", example="", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"), * @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),

View File

@ -29,6 +29,7 @@
* @OA\Property(property="line_items", type="object", example="", description="_________"), * @OA\Property(property="line_items", type="object", example="", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"), * @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),

View File

@ -35,6 +35,7 @@
* @OA\Property(property="custom_value4", type="string", example="", description="________"), * @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="vat_number", type="string", example="", description="________"), * @OA\Property(property="vat_number", type="string", example="", description="________"),
* @OA\Property(property="id_number", type="string", example="", description="________"), * @OA\Property(property="id_number", type="string", example="", description="________"),
* @OA\Property(property="number", type="string", example="", description="________"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="________"), * @OA\Property(property="is_deleted", type="boolean", example=true, description="________"),
* @OA\Property(property="last_login", type="number", format="integer", example="134341234234", description="Timestamp"), * @OA\Property(property="last_login", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"), * @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),

View File

@ -435,11 +435,6 @@ class PaymentController extends BaseController
*/ */
public function destroy(DestroyPaymentRequest $request, Payment $payment) public function destroy(DestroyPaymentRequest $request, Payment $payment)
{ {
// $payment->service()->deletePayment();
// $payment->is_deleted = true;
// $payment->save();
// $payment->delete();
$this->payment_repo->delete($payment); $this->payment_repo->delete($payment);

View File

@ -74,7 +74,7 @@ class PingController extends BaseController
public function health() public function health()
{ {
if (Ninja::isNinja()) { if (Ninja::isNinja()) {
return response()->json(['message' => 'Route not available', 'errors'=>[]], 403); return response()->json(['message' => ctrans('texts.route_not_available'), 'errors'=>[]], 403);
} }
return response()->json(SystemHealth::check(), 200); return response()->json(SystemHealth::check(), 200);

View File

@ -81,7 +81,7 @@ class PreviewController extends BaseController
$design_object = json_decode(json_encode(request()->input('design'))); $design_object = json_decode(json_encode(request()->input('design')));
if (! is_object($design_object)) { if (! is_object($design_object)) {
return response()->json(['message' => 'Invalid custom design object'], 400); return response()->json(['message' => ctrans('texts.invalid_design_object')], 400);
} }
$entity = ucfirst(request()->input('entity')); $entity = ucfirst(request()->input('entity'));

View File

@ -507,7 +507,7 @@ class QuoteController extends BaseController
$quotes = Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); $quotes = Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $quotes) { if (! $quotes) {
return response()->json(['message' => 'No Quote/s Found']); return response()->json(['message' => ctrans('texts.quote_not_found')]);
} }
/* /*
@ -517,13 +517,13 @@ class QuoteController extends BaseController
if ($action == 'download' && $quotes->count() >= 1) { if ($action == 'download' && $quotes->count() >= 1) {
$quotes->each(function ($quote) { $quotes->each(function ($quote) {
if (auth()->user()->cannot('view', $quote)) { if (auth()->user()->cannot('view', $quote)) {
return response()->json(['message'=>'Insufficient privileges to access quote '.$quote->number]); return response()->json(['message'=> ctrans('texts.access_denied')]);
} }
}); });
ZipInvoices::dispatch($quotes, $quotes->first()->company, auth()->user()->email); ZipInvoices::dispatch($quotes, $quotes->first()->company, auth()->user()->email);
return response()->json(['message' => 'Email Sent!'], 200); return response()->json(['message' => ctrans('texts.sent_message')], 200);
} }
if ($action == 'convert') { if ($action == 'convert') {
@ -651,7 +651,7 @@ class QuoteController extends BaseController
case 'approve': case 'approve':
//make sure it hasn't already been approved!! //make sure it hasn't already been approved!!
if ($quote->status_id != Quote::STATUS_SENT) { if ($quote->status_id != Quote::STATUS_SENT) {
return response()->json(['message' => 'Unable to approve this quote as it has expired.'], 400); return response()->json(['message' => ctrans('texts.quote_unapprovable')], 400);
} }
return $this->itemResponse($quote->service()->approve()->save()); return $this->itemResponse($quote->service()->approve()->save());
@ -692,7 +692,7 @@ class QuoteController extends BaseController
case 'email': case 'email':
$quote->service()->sendEmail(); $quote->service()->sendEmail();
return response()->json(['message'=>'email sent'], 200); return response()->json(['message'=> ctrans('texts.sent_message')], 200);
break; break;
case 'mark_sent': case 'mark_sent':
$quote->service()->markSent()->save(); $quote->service()->markSent()->save();
@ -702,7 +702,7 @@ class QuoteController extends BaseController
} }
break; break;
default: default:
return response()->json(['message' => "The requested action `{$action}` is not available."], 400); return response()->json(['message' => ctrans('texts.action_unavailable',['action' => $action])], 400);
break; break;
} }
} }

View File

@ -7,9 +7,9 @@ class SchedulerController extends Controller
public function index() public function index()
{ {
if (auth()->user()->company()->account->latest_version == '0.0.0') { if (auth()->user()->company()->account->latest_version == '0.0.0') {
return response()->json(['message' => 'Scheduler has never run'], 400); return response()->json(['message' => ctrans('texts.scheduler_has_never_run')], 400);
} else { } else {
return response()->json(['message' => 'Scheduler has run'], 200); return response()->json(['message' => ctrans('texts.scheduler_has_run')], 200);
} }
} }
} }

View File

@ -59,7 +59,7 @@ class SelfUpdateController extends BaseController
define('STDIN', fopen('php://stdin', 'r')); define('STDIN', fopen('php://stdin', 'r'));
if (Ninja::isNinja()) { if (Ninja::isNinja()) {
return response()->json(['message' => 'Self update not available on this system.'], 403); return response()->json(['message' => ctrans('texts.self_update_not_available')], 403);
} }
/* .git MUST be owned/writable by the webserver user */ /* .git MUST be owned/writable by the webserver user */

View File

@ -1,101 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
use Illuminate\Http\Request;
/**
* Class SettingsController.
*/
class SettingsController extends BaseController
{
public function __construct()
{
parent::__construct();
}
/**
* Display a listing of the resource.
*
* @return void
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return void
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return void
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return void
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return void
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param int $id
* @return void
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return void
*/
public function destroy($id)
{
//
}
}

View File

@ -1,123 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
/**
* Class TranslationController.
*/
class TranslationController extends BaseController
{
public function __construct()
{
parent::__construct();
}
/**
* Display a listing of the resource.
*
* @return void
*/
public function index()
{
$strings = Cache::rememberForever('lang.js', function () {
$lang = config('app.locale');
$files = glob(resource_path('lang/'.$lang.'/*.php'));
$strings = [];
foreach ($files as $file) {
$name = basename($file, '.php');
$strings[$name] = require $file;
}
return $strings;
});
header('Content-Type: text/javascript');
echo 'i18n = '.$this->easyMinify(json_encode($strings)).';';
exit();
}
private function easyMinify($javascript)
{
return preg_replace(["/\s+\n/", "/\n\s+/", '/ +/'], ["\n", "\n ", ' '], $javascript);
}
/**
* Show the form for creating a new resource.
*
* @return void
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return void
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return void
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return void
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param int $id
* @return void
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return void
*/
public function destroy($id)
{
//
}
}

View File

@ -668,6 +668,6 @@ class UserController extends BaseController
$company_user->delete(); $company_user->delete();
} }
return response()->json(['message' => 'User detached from company'], 200); return response()->json(['message' => ctrans('texts.user_detached')], 200);
} }
} }

View File

@ -364,7 +364,7 @@ class WebhookController extends BaseController
$webhook->save(); $webhook->save();
if (! $webhook->id) { if (! $webhook->id) {
return response()->json('Failed to create Webhook', 400); return response()->json(ctrans('texts.create_webhook_failure'), 400);
} }
return $this->itemResponse($webhook); return $this->itemResponse($webhook);

View File

@ -65,6 +65,8 @@ class RequiredClientInfo extends Component
public $show_form = false; public $show_form = false;
public function mount() {}
public function handleSubmit(array $data): bool public function handleSubmit(array $data): bool
{ {
$rules = []; $rules = [];

View File

@ -25,13 +25,4 @@ class EditClientRequest extends Request
return auth()->user()->can('edit', $this->client); return auth()->user()->can('edit', $this->client);
} }
// public function prepareForValidation()
// {
// $input = $this->all();
// //$input['id'] = $this->encodePrimaryKey($input['id']);
// $this->replace($input);
// }
} }

View File

@ -32,51 +32,6 @@ class StoreQuoteRequest extends Request
return auth()->user()->can('create', Quote::class); return auth()->user()->can('create', Quote::class);
} }
protected function prepareForValidation()
{
$input = $this->all();
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if ($input['client_id']) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (isset($input['client_contacts'])) {
foreach ($input['client_contacts'] as $key => $contact) {
if (! array_key_exists('send_email', $contact) || ! array_key_exists('id', $contact)) {
unset($input['client_contacts'][$key]);
}
}
}
if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) {
if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']);
}
if (isset($input['invitations'][$key]['id']) && is_string($input['invitations'][$key]['id'])) {
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['client_contact_id'])) {
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
}
}
}
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']);
$this->replace($input);
}
public function rules() public function rules()
{ {
$rules = []; $rules = [];
@ -98,4 +53,17 @@ class StoreQuoteRequest extends Request
return $rules; return $rules;
} }
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($input);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = 0;
$input['balance'] = 0;
$this->replace($input);
}
} }

View File

@ -59,22 +59,12 @@ class UpdateQuoteRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { $input = $this->decodePrimaryKeys($input);
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (isset($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (isset($input['line_items'])) { if (isset($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
} }
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (array_key_exists('documents', $input)) { if (array_key_exists('documents', $input)) {
unset($input['documents']); unset($input['documents']);
} }

View File

@ -33,6 +33,6 @@ class ValidCompanyQuantity implements Rule
*/ */
public function message() public function message()
{ {
return 'Limit of 10 companies per account.'; return ctrans('texts.company_limit_reached');
} }
} }

View File

@ -48,6 +48,6 @@ class CreditsSumRule implements Rule
*/ */
public function message() public function message()
{ {
return "Total credits applied cannot be MORE than total of invoices"; return ctrans('texts.credits_applied_validation');
} }
} }

View File

@ -41,7 +41,7 @@ class UniqueCreditNumberRule implements Rule
*/ */
public function message() public function message()
{ {
return 'Credit number already taken'; return ctrans('texts.credit_number_taken');
} }
/** /**

View File

@ -44,7 +44,8 @@ class ValidCreditsRules implements Rule
private function checkCreditsAreHomogenous() private function checkCreditsAreHomogenous()
{ {
if (! array_key_exists('client_id', $this->input)) { if (! array_key_exists('client_id', $this->input)) {
$this->error_msg = 'Client id is required';
$this->error_msg = ctrans('texts.client_id_required');
return false; return false;
} }
@ -57,44 +58,32 @@ class ValidCreditsRules implements Rule
$cred = Credit::find($this->decodePrimaryKey($credit['credit_id'])); $cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
if (! $cred) { if (! $cred) {
$this->error_msg = 'Credit not found '; $this->error_msg = ctrans('texts.credit_not_found');
return false; return false;
} }
if ($cred->client_id != $this->input['client_id']) { if ($cred->client_id != $this->input['client_id']) {
$this->error_msg = 'Selected invoices are not from a single client'; $this->error_msg = ctrans('texts.invoices_dont_match_client');
return false; return false;
} }
} }
if (! (array_unique($unique_array) == $unique_array)) { if (! (array_unique($unique_array) == $unique_array)) {
$this->error_msg = 'Duplicate credits submitted.'; $this->error_msg = ctrans('texts.duplicate_credits_submitted');
return false; return false;
} }
if (count($this->input['credits']) >= 1 && count($this->input['invoices']) == 0) { if (count($this->input['credits']) >= 1 && count($this->input['invoices']) == 0) {
$this->error_msg = 'You must have an invoice set when using a credit in a payment'; $this->error_msg = ctrans('texts.credit_with_no_invoice');
return false; return false;
} }
if (count($this->input['credits']) >= 1) { if (count($this->input['credits']) >= 1) {
// $total_payments = $this->input['amount'] + array_sum(array_column($this->input['credits'], 'amount'));
// nlog(print_r($this->input,1));
// nlog("total payments = {$total_payments}");
// nlog("total credits available = " . array_sum(array_column($this->input['credits'], 'amount')));
// nlog("total invoices payable = " . array_sum(array_column($this->input['invoices'], 'amount')));
// if($total_payments > array_sum(array_column($this->input['invoices'], 'amount'))){
// $this->error_msg = "Sum of total payments and credits is greater than the total of invoices";
// return false;
// }
} }
return true; return true;

View File

@ -41,7 +41,7 @@ class UniqueExpenseNumberRule implements Rule
*/ */
public function message() public function message()
{ {
return 'Expense number already taken'; return ctrans('texts.expense_number_taken');
} }
/** /**
@ -57,20 +57,7 @@ class UniqueExpenseNumberRule implements Rule
->where('number', $this->input['number']) ->where('number', $this->input['number'])
->withTrashed(); ->withTrashed();
// if(isset($this->input['client_id']))
// $expense->where('client_id', $this->input['client_id']);
return $expense->exists(); return $expense->exists();
// $expense = Expense::where('client_id', $this->input['client_id'])
// ->where('number', $this->input['number'])
// ->withTrashed()
// ->exists();
// if ($expense) {
// return false;
// }
// return true;
} }
} }

View File

@ -41,7 +41,7 @@ class UniqueInvoiceNumberRule implements Rule
*/ */
public function message() public function message()
{ {
return 'Invoice number already taken'; return ctrans('texts.invoice_number_taken');
} }
/** /**

View File

@ -44,7 +44,8 @@ class ValidInvoicesRules implements Rule
private function checkInvoicesAreHomogenous() private function checkInvoicesAreHomogenous()
{ {
if (! array_key_exists('client_id', $this->input)) { if (! array_key_exists('client_id', $this->input)) {
$this->error_msg = 'Client id is required';
$this->error_msg = ctrans('texts.client_id_required');
return false; return false;
} }
@ -60,14 +61,14 @@ class ValidInvoicesRules implements Rule
if (! $inv) { if (! $inv) {
$this->error_msg = 'Invoice not found '; $this->error_msg = ctrans('texts.invoice_not_found');
return false; return false;
} }
if ($inv->client_id != $this->input['client_id']) { if ($inv->client_id != $this->input['client_id']) {
$this->error_msg = 'Selected invoices are not from a single client'; $this->error_msg = ctrans('texts.invoices_dont_match_client');
return false; return false;
} }
@ -83,14 +84,14 @@ class ValidInvoicesRules implements Rule
} }
else if($invoice['amount'] > $inv->balance) { else if($invoice['amount'] > $inv->balance) {
$this->error_msg = 'Amount cannot be greater than invoice balance'; $this->error_msg = ctrans('texts.amount_greater_than_balance');
return false; return false;
} }
} }
if (! (array_unique($unique_array) == $unique_array)) { if (! (array_unique($unique_array) == $unique_array)) {
$this->error_msg = 'Duplicate invoices submitted.'; $this->error_msg = ctrans('texts.duplicate_invoices_submitted');
return false; return false;
} }

View File

@ -41,7 +41,7 @@ class ValidRefundableRequest implements Rule
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
if (! array_key_exists('id', $this->input)) { if (! array_key_exists('id', $this->input)) {
$this->error_msg = 'Payment `id` required.'; $this->error_msg = ctrans('texts.payment_id_required');
return false; return false;
} }
@ -49,7 +49,7 @@ class ValidRefundableRequest implements Rule
$payment = Payment::whereId($this->input['id'])->first(); $payment = Payment::whereId($this->input['id'])->first();
if (! $payment) { if (! $payment) {
$this->error_msg = 'Unable to retrieve specified payment'; $this->error_msg = ctrans('texts.unable_to_retrieve_payment');
return false; return false;
} }
@ -57,31 +57,17 @@ class ValidRefundableRequest implements Rule
$request_invoices = request()->has('invoices') ? $this->input['invoices'] : []; $request_invoices = request()->has('invoices') ? $this->input['invoices'] : [];
$request_credits = request()->has('credits') ? $this->input['credits'] : []; $request_credits = request()->has('credits') ? $this->input['credits'] : [];
// foreach($request_invoices as $key => $value)
// $request_invoices[$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
// foreach($request_credits as $key => $value)
// $request_credits[$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
if ($payment->invoices()->exists()) { if ($payment->invoices()->exists()) {
foreach ($payment->invoices as $paymentable_invoice) { foreach ($payment->invoices as $paymentable_invoice) {
$this->checkInvoice($paymentable_invoice, $request_invoices); $this->checkInvoice($paymentable_invoice, $request_invoices);
} }
} }
// if($payment->credits()->exists())
// {
// foreach($payment->credits as $paymentable_credit)
// $this->checkCredit($paymentable_credit, $request_credits);
// }
foreach ($request_invoices as $request_invoice) { foreach ($request_invoices as $request_invoice) {
$this->checkInvoiceIsPaymentable($request_invoice, $payment); $this->checkInvoiceIsPaymentable($request_invoice, $payment);
} }
// foreach($request_credits as $request_credit)
// $this->checkCreditIsPaymentable($request_credit, $payment);
if (strlen($this->error_msg) > 0) { if (strlen($this->error_msg) > 0) {
return false; return false;
} }
@ -97,12 +83,12 @@ class ValidRefundableRequest implements Rule
$paymentable_invoice = $payment->invoices->where('id', $invoice->id)->first(); $paymentable_invoice = $payment->invoices->where('id', $invoice->id)->first();
if (! $paymentable_invoice) { if (! $paymentable_invoice) {
$this->error_msg = 'Invoice id '.$invoice->hashed_id.' is not related to this payment'; $this->error_msg = ctrans('texts.invoice_not_related_to_payment', ['invoice' => $invoice->hashed_id]);
return false; return false;
} }
} else { } else {
$this->error_msg = 'Invoice id '.$invoice->hashed_id.' is not related to this payment'; $this->error_msg = ctrans('texts.invoice_not_related_to_payment', ['invoice' => $invoice->hashed_id]);
return false; return false;
} }
@ -116,12 +102,12 @@ class ValidRefundableRequest implements Rule
$paymentable_credit = $payment->credits->where('id', $credit->id)->first(); $paymentable_credit = $payment->credits->where('id', $credit->id)->first();
if (! $paymentable_invoice) { if (! $paymentable_invoice) {
$this->error_msg = 'Credit id '.$credit->hashed_id.' is not related to this payment'; $this->error_msg = ctrans('texts.credit_not_related_to_payment', ['credit' => $credit->hashed_id]);
return false; return false;
} }
} else { } else {
$this->error_msg = 'Credit id '.$credit->hashed_id.' is not related to this payment'; $this->error_msg = ctrans('texts.credit_not_related_to_payment', ['credit' => $credit->hashed_id]);
return false; return false;
} }
@ -140,7 +126,7 @@ class ValidRefundableRequest implements Rule
if ($request_invoice['amount'] > $refundable_amount) { if ($request_invoice['amount'] > $refundable_amount) {
$invoice = $paymentable; $invoice = $paymentable;
$this->error_msg = 'Attempting to refund more than allowed for invoice id '.$invoice->hashed_id.', maximum refundable amount is '.$refundable_amount; $this->error_msg = ctrans('texts.max_refundable_invoice', ['invoice' => $invoice->hashed_id, 'amount' => $refundable_amount]);
return false; return false;
} }
@ -148,7 +134,7 @@ class ValidRefundableRequest implements Rule
} }
if (! $record_found) { if (! $record_found) {
$this->error_msg = 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.'; $this->error_msg = ctrans('texts.refund_without_invoices');
return false; return false;
} }
@ -167,7 +153,7 @@ class ValidRefundableRequest implements Rule
if ($request_credit['amount'] > $refundable_amount) { if ($request_credit['amount'] > $refundable_amount) {
$credit = $paymentable; $credit = $paymentable;
$this->error_msg = 'Attempting to refund more than allowed for credit '.$credit->number.', maximum refundable amount is '.$refundable_amount; $this->error_msg = ctrans('texts.max_refundable_credit',['credit' => $credit->hashed_id, 'amount' => $refundable_amount]);
return false; return false;
} }
@ -175,7 +161,7 @@ class ValidRefundableRequest implements Rule
} }
if (! $record_found) { if (! $record_found) {
$this->error_msg = 'Attempting to refund a payment with credits attached, please specify valid credit/s to be refunded.'; $this->error_msg = ctrans('texts.refund_without_credits');
return false; return false;
} }

View File

@ -33,7 +33,7 @@ class PaymentAmountsBalanceRule implements Rule
*/ */
public function message() public function message()
{ {
return 'Amounts do not balance correctly.'; return ctrans('texts.amounts_do_not_balance');
} }
private function calculateAmounts() :bool private function calculateAmounts() :bool
@ -69,10 +69,8 @@ class PaymentAmountsBalanceRule implements Rule
} }
} else { } else {
return true; return true;
} // if no invoices are present, then this is an unapplied payment, let this pass validation! }
// nlog("payment amounts = {$payment_amounts}");
// nlog("invoice amounts = {$invoice_amounts}");
return $payment_amounts >= $invoice_amounts; return $payment_amounts >= $invoice_amounts;
} }

View File

@ -37,7 +37,7 @@ class PaymentAppliedValidAmount implements Rule
*/ */
public function message() public function message()
{ {
return 'Insufficient applied amount remaining to cover payment.'; return ctrans('texts.insufficient_applied_amount_remaining');
} }
private function calculateAmounts() :bool private function calculateAmounts() :bool

View File

@ -53,6 +53,6 @@ class ValidProjectForClient implements Rule
*/ */
public function message() public function message()
{ {
return "Project client does not match entity client"; return ctrans('texts.project_client_do_not_match');
} }
} }

View File

@ -41,7 +41,7 @@ class UniqueQuoteNumberRule implements Rule
*/ */
public function message() public function message()
{ {
return 'Quote number already taken'; return ctrans('texts.quote_number_taken');
} }
/** /**

View File

@ -41,7 +41,7 @@ class UniqueRecurringInvoiceNumberRule implements Rule
*/ */
public function message() public function message()
{ {
return "Recurring Invoice number {$this->input['number']} already taken"; return ctrans('texts.recurring_invoice_number_taken', ['number' => $this->input['number']]);
} }
/** /**

View File

@ -40,7 +40,7 @@ class RelatedUserRule implements Rule
*/ */
public function message() public function message()
{ {
return 'User not associated with this account'; return ctrans('texts.user_not_associated_with_account');
} }
/** /**

View File

@ -37,26 +37,13 @@ class ValidCreditsPresentRule implements Rule
*/ */
public function message() public function message()
{ {
return 'Insufficient balance on credit.'; return ctrans('texts.insufficient_credit_balance');
} }
private function validCreditsPresent() :bool private function validCreditsPresent() :bool
{ {
//todo need to ensure the clients credits are here not random ones! //todo need to ensure the clients credits are here not random ones!
// if (request()->input('credits') && is_array(request()->input('credits'))) {
// foreach (request()->input('credits') as $credit) {
// $cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
// if (! $cred || $cred->balance == 0) {
// return false;
// }
// }
// }
// return true;
if (request()->input('credits') && is_array(request()->input('credits'))) { if (request()->input('credits') && is_array(request()->input('credits'))) {
$credit_collection = Credit::whereIn('id', $this->transformKeys(array_column(request()->input('credits'), 'credit_id'))) $credit_collection = Credit::whereIn('id', $this->transformKeys(array_column(request()->input('credits'), 'credit_id')))
->where('balance', '>', 0) ->where('balance', '>', 0)

View File

@ -40,7 +40,7 @@ class ValidPayableInvoicesRule implements Rule
foreach ($invoices as $invoice) { foreach ($invoices as $invoice) {
if (! $invoice->isPayable()) { if (! $invoice->isPayable()) {
$this->error_msg = 'One or more of these invoices have been paid'; $this->error_msg = ctrans('texts.one_or_more_invoices_paid');
return false; return false;
} }

View File

@ -40,7 +40,7 @@ class ValidRefundableInvoices implements Rule
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
if (! array_key_exists('id', $this->input)) { if (! array_key_exists('id', $this->input)) {
$this->error_msg = 'Payment `id` required.'; $this->error_msg = ctrans('texts.payment_id_required');
return false; return false;
} }
@ -48,17 +48,11 @@ class ValidRefundableInvoices implements Rule
$payment = Payment::whereId($this->input['id'])->first(); $payment = Payment::whereId($this->input['id'])->first();
if (! $payment) { if (! $payment) {
$this->error_msg = "Payment couldn't be retrieved cannot be refunded "; $this->error_msg = ctrans('texts.unable_to_retrieve_payment');
return false; return false;
} }
/*We are not sending the Refunded amount in the 'amount field, this is the Payment->amount, need to skip this check. */
// if (request()->has('amount') && (request()->input('amount') > ($payment->amount - $payment->refunded))) {
// $this->error_msg = "Attempting to refund more than payment amount, enter a value equal to or lower than the payment amount of ". $payment->amount;
// return false;
// }
/*If no invoices has been sent, then we apply the payment to the client account*/ /*If no invoices has been sent, then we apply the payment to the client account*/
$invoices = []; $invoices = [];
@ -70,7 +64,7 @@ class ValidRefundableInvoices implements Rule
foreach ($invoices as $invoice) { foreach ($invoices as $invoice) {
if (! $invoice->isRefundable()) { if (! $invoice->isRefundable()) {
$this->error_msg = 'Invoice id '.$invoice->hashed_id.' cannot be refunded'; $this->error_msg = ctrans('texts.invoice_cannot_be_refunded', ['invoice' => $invoice->hashed_id]);
return false; return false;
} }
@ -82,7 +76,7 @@ class ValidRefundableInvoices implements Rule
$pivot_record = $payment->paymentables->where('paymentable_id', $invoice->id)->first(); $pivot_record = $payment->paymentables->where('paymentable_id', $invoice->id)->first();
if ($val['amount'] > ($pivot_record->amount - $pivot_record->refunded)) { if ($val['amount'] > ($pivot_record->amount - $pivot_record->refunded)) {
$this->error_msg = 'Attempting to refund '.$val['amount'].' only '.($pivot_record->amount - $pivot_record->refunded).' available for refund'; $this->error_msg = ctrans('texts.attempted_refund_failed', ['amount' => $val['amount'], 'refundable_amount' => ($pivot_record->amount - $pivot_record->refunded)]);
return false; return false;
} }

View File

@ -34,7 +34,6 @@ class ValidUserForCompany implements Rule
*/ */
public function message() public function message()
{ {
return 'This user is unable to be attached to this company. Perhaps they have already registered a user on another account?'; return ctrans('texts.user_not_associated_with_this_account');
//return ctrans('texts.email_already_register');
} }
} }

View File

@ -78,8 +78,8 @@ class ClientMap
16 => 'texts.custom_value', 16 => 'texts.custom_value',
17 => 'texts.custom_value', 17 => 'texts.custom_value',
18 => 'texts.custom_value', 18 => 'texts.custom_value',
19 => 'texts.address1', 19 => 'texts.shipping_address1',
20 => 'texts.address2', 20 => 'texts.shipping_address2',
21 => 'texts.shipping_city', 21 => 'texts.shipping_city',
22 => 'texts.shipping_state', 22 => 'texts.shipping_state',
23 => 'texts.shipping_postal_code', 23 => 'texts.shipping_postal_code',

View File

@ -57,17 +57,31 @@ class ApplyCreditPayment implements ShouldQueue
if ($cred->id == $this->credit->id) { if ($cred->id == $this->credit->id) {
$cred->pivot->amount = $this->amount; $cred->pivot->amount = $this->amount;
$cred->pivot->save(); $cred->pivot->save();
$cred->paid_to_date += $this->amount;
$cred->save();
} }
}); });
$credit_balance = $this->credit->balance; $credit_balance = $this->credit->balance;
if ($this->amount == $credit_balance) { //total credit applied. if ($this->amount == $credit_balance) { //total credit applied.
$this->credit->setStatus(Credit::STATUS_APPLIED);
$this->credit->updateBalance($this->amount * -1); $this->credit
->service()
->setStatus(Credit::STATUS_APPLIED)
->adjustBalance($this->amount * -1)
->updatePaidToDate($this->amount)
->save();
} elseif ($this->amount < $credit_balance) { //compare number appropriately } elseif ($this->amount < $credit_balance) { //compare number appropriately
$this->credit->setStatus(Credit::STATUS_PARTIAL);
$this->credit->updateBalance($this->amount * -1); $this->credit
->service()
->setStatus(Credit::STATUS_PARTIAL)
->adjustBalance($this->amount * -1)
->updatePaidToDate($this->amount)
->save();
} }
/* Update Payment Applied Amount*/ /* Update Payment Applied Amount*/

View File

@ -896,14 +896,6 @@ class Import implements ShouldQueue
], ],
]; ];
//depending on the status, we do a final action.
//s$payment = $this->updatePaymentForStatus($payment, $modified['status_id']);
// if($modified['is_deleted'])
// $payment->service()->deletePayment();
// if(isset($modified['deleted_at']))
// $payment->delete();
} }
Payment::reguard(); Payment::reguard();

View File

@ -49,6 +49,6 @@ class CreditArchivedActivity implements ShouldQueue
$fields->company_id = $event->credit->company_id; $fields->company_id = $event->credit->company_id;
$fields->activity_type_id = Activity::ARCHIVE_CREDIT; $fields->activity_type_id = Activity::ARCHIVE_CREDIT;
$this->activity_repo->save($fields, $$event->credit, $event->event_vars); $this->activity_repo->save($fields, $event->credit, $event->event_vars);
} }
} }

View File

@ -47,7 +47,8 @@ class InvoicePaidActivity implements ShouldQueue
$fields->user_id = $event->invoice->user_id; $fields->user_id = $event->invoice->user_id;
$fields->company_id = $event->invoice->company_id; $fields->company_id = $event->invoice->company_id;
$fields->activity_type_id = Activity::PAID_INVOICE; $fields->activity_type_id = Activity::PAID_INVOICE;
$fields->payment_id = $event->payment->id;
$this->activity_repo->save($fields, $event->invoice, $event->event_vars); $this->activity_repo->save($fields, $event->invoice, $event->event_vars);
try { try {

View File

@ -123,6 +123,7 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$address1'] = ['value' => $this->client->address1 ?: '&nbsp;', 'label' => ctrans('texts.address1')]; $data['$address1'] = ['value' => $this->client->address1 ?: '&nbsp;', 'label' => ctrans('texts.address1')];
$data['$address2'] = ['value' => $this->client->address2 ?: '&nbsp;', 'label' => ctrans('texts.address2')]; $data['$address2'] = ['value' => $this->client->address2 ?: '&nbsp;', 'label' => ctrans('texts.address2')];
$data['$id_number'] = ['value' => $this->client->id_number ?: '&nbsp;', 'label' => ctrans('texts.id_number')]; $data['$id_number'] = ['value' => $this->client->id_number ?: '&nbsp;', 'label' => ctrans('texts.id_number')];
$data['$client.number'] = ['value' => $this->client->number ?: '&nbsp;', 'label' => ctrans('texts.number')];
$data['$vat_number'] = ['value' => $this->client->vat_number ?: '&nbsp;', 'label' => ctrans('texts.vat_number')]; $data['$vat_number'] = ['value' => $this->client->vat_number ?: '&nbsp;', 'label' => ctrans('texts.vat_number')];
$data['$website'] = ['value' => $this->client->present()->website() ?: '&nbsp;', 'label' => ctrans('texts.website')]; $data['$website'] = ['value' => $this->client->present()->website() ?: '&nbsp;', 'label' => ctrans('texts.website')];
$data['$phone'] = ['value' => $this->client->present()->phone() ?: '&nbsp;', 'label' => ctrans('texts.phone')]; $data['$phone'] = ['value' => $this->client->present()->phone() ?: '&nbsp;', 'label' => ctrans('texts.phone')];

View File

@ -73,7 +73,8 @@ class Client extends BaseModel implements HasLocalePreference
'id_number', 'id_number',
'group_settings_id', 'group_settings_id',
'public_notes', 'public_notes',
'phone' 'phone',
'number',
]; ];
protected $with = [ protected $with = [

View File

@ -261,4 +261,5 @@ class Credit extends BaseModel
} }
}); });
} }
} }

View File

@ -44,6 +44,7 @@ class Vendor extends BaseModel
'custom_value2', 'custom_value2',
'custom_value3', 'custom_value3',
'custom_value4', 'custom_value4',
'number',
]; ];
protected $casts = [ protected $casts = [

View File

@ -17,7 +17,9 @@ use App\Models\Client;
use App\PaymentDrivers\AuthorizePaymentDriver; use App\PaymentDrivers\AuthorizePaymentDriver;
use net\authorize\api\contract\v1\CreateCustomerProfileRequest; use net\authorize\api\contract\v1\CreateCustomerProfileRequest;
use net\authorize\api\contract\v1\CustomerProfileType; use net\authorize\api\contract\v1\CustomerProfileType;
use net\authorize\api\contract\v1\GetCustomerProfileRequest;
use net\authorize\api\controller\CreateCustomerProfileController; use net\authorize\api\controller\CreateCustomerProfileController;
use net\authorize\api\controller\GetCustomerProfileController;
/** /**
* Class BaseDriver. * Class BaseDriver.
@ -75,4 +77,46 @@ class AuthorizeCreateCustomer
throw new GenericPaymentDriverFailure($message); throw new GenericPaymentDriverFailure($message);
} }
} }
public function get($profileIdRequested)
{
error_reporting(E_ALL & ~E_DEPRECATED);
$this->authorize->init();
$request = new GetCustomerProfileRequest();
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
$request->setCustomerProfileId($profileIdRequested);
$controller = new GetCustomerProfileController($request);
$response = $controller->executeWithApiResponse($this->authorize->merchant_authentication);
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok") )
{
echo "GetCustomerProfile SUCCESS : " . "\n";
$profileSelected = $response->getProfile();
$paymentProfilesSelected = $profileSelected->getPaymentProfiles();
echo "Profile Has " . count($paymentProfilesSelected). " Payment Profiles" . "\n";
if($response->getSubscriptionIds() != null)
{
if($response->getSubscriptionIds() != null)
{
echo "List of subscriptions:";
foreach($response->getSubscriptionIds() as $subscriptionid)
echo $subscriptionid . "\n";
}
}
}
else
{
echo "ERROR : GetCustomerProfile: Invalid response\n";
$errorMessages = $response->getMessages()->getMessage();
echo "Response : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() . "\n";
}
return $response;
}
} }

View File

@ -68,7 +68,7 @@ class AuthorizeCreditCard
$payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data); $payment_profile = $authorise_payment_method->addPaymentMethodToClient($gateway_customer_reference, $data);
$payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId(); $payment_profile_id = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
if ($request->has('store_card') && $request->input('store_card') === 'true') { if ($request->has('store_card') && $request->input('store_card') === true) {
$authorise_payment_method->payment_method = GatewayType::CREDIT_CARD; $authorise_payment_method->payment_method = GatewayType::CREDIT_CARD;
$client_gateway_token = $authorise_payment_method->createClientGatewayToken($payment_profile, $gateway_customer_reference); $client_gateway_token = $authorise_payment_method->createClientGatewayToken($payment_profile, $gateway_customer_reference);
} }
@ -80,7 +80,7 @@ class AuthorizeCreditCard
private function processTokenPayment($request) private function processTokenPayment($request)
{ {
$client_gateway_token = ClientGatewayToken::find($this->decodePrimaryKey($request->token)); $client_gateway_token =ClientGatewayToken::where('token', $request->token)->firstOrFail();
$data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($client_gateway_token->gateway_customer_reference, $client_gateway_token->token, $request->input('amount_with_fee')); $data = (new ChargePaymentProfile($this->authorize))->chargeCustomerProfile($client_gateway_token->gateway_customer_reference, $client_gateway_token->token, $request->input('amount_with_fee'));

View File

@ -13,15 +13,18 @@
namespace App\PaymentDrivers\Authorize; namespace App\PaymentDrivers\Authorize;
use App\Exceptions\GenericPaymentDriverFailure; use App\Exceptions\GenericPaymentDriverFailure;
use App\Exceptions\PaymentFailed;
use App\Models\GatewayType; use App\Models\GatewayType;
use App\PaymentDrivers\AuthorizePaymentDriver; use App\PaymentDrivers\AuthorizePaymentDriver;
use net\authorize\api\contract\v1\CreateCustomerPaymentProfileRequest; use net\authorize\api\contract\v1\CreateCustomerPaymentProfileRequest;
use net\authorize\api\contract\v1\CustomerAddressType; use net\authorize\api\contract\v1\CustomerAddressType;
use net\authorize\api\contract\v1\CustomerPaymentProfileType; use net\authorize\api\contract\v1\CustomerPaymentProfileType;
use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest;
use net\authorize\api\contract\v1\GetCustomerPaymentProfileRequest; use net\authorize\api\contract\v1\GetCustomerPaymentProfileRequest;
use net\authorize\api\contract\v1\OpaqueDataType; use net\authorize\api\contract\v1\OpaqueDataType;
use net\authorize\api\contract\v1\PaymentType; use net\authorize\api\contract\v1\PaymentType;
use net\authorize\api\controller\CreateCustomerPaymentProfileController; use net\authorize\api\controller\CreateCustomerPaymentProfileController;
use net\authorize\api\controller\DeleteCustomerPaymentProfileController;
use net\authorize\api\controller\GetCustomerPaymentProfileController; use net\authorize\api\controller\GetCustomerPaymentProfileController;
use stdClass; use stdClass;
@ -94,12 +97,14 @@ class AuthorizePaymentMethod
if ($client_gateway_token = $this->authorize->findClientGatewayRecord()) { if ($client_gateway_token = $this->authorize->findClientGatewayRecord()) {
$payment_profile = $this->addPaymentMethodToClient($client_gateway_token->gateway_customer_reference, $data); $payment_profile = $this->addPaymentMethodToClient($client_gateway_token->gateway_customer_reference, $data);
$gateway_customer_reference = $client_gateway_token->gateway_customer_reference;
} else { } else {
$gateway_customer_reference = (new AuthorizeCreateCustomer($this->authorize, $this->authorize->client))->create($data); $gateway_customer_reference = (new AuthorizeCreateCustomer($this->authorize, $this->authorize->client))->create($data);
$payment_profile = $this->addPaymentMethodToClient($gateway_customer_reference, $data); $payment_profile = $this->addPaymentMethodToClient($gateway_customer_reference, $data);
$this->createClientGatewayToken($payment_profile, $gateway_customer_reference);
} }
$this->createClientGatewayToken($payment_profile, $gateway_customer_reference);
return redirect()->route('client.payment_methods.index'); return redirect()->route('client.payment_methods.index');
} }
@ -116,6 +121,7 @@ class AuthorizePaymentMethod
$data['token'] = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId(); $data['token'] = $payment_profile->getPaymentProfile()->getCustomerPaymentProfileId();
$data['payment_method_id'] = $this->payment_method_id; $data['payment_method_id'] = $this->payment_method_id;
$data['payment_meta'] = $this->buildPaymentMethod($payment_profile); $data['payment_meta'] = $this->buildPaymentMethod($payment_profile);
$data['payment_method_id'] = GatewayType::CREDIT_CARD;
$additional['gateway_customer_reference'] = $gateway_customer_reference; $additional['gateway_customer_reference'] = $gateway_customer_reference;
@ -206,7 +212,8 @@ class AuthorizePaymentMethod
$message = $errorMessages[0]->getCode().' '.$errorMessages[0]->getText(); $message = $errorMessages[0]->getCode().' '.$errorMessages[0]->getText();
} }
throw new GenericPaymentDriverFailure($message); throw new PaymentFailed($message, 500);
} }
} }
@ -245,4 +252,37 @@ class AuthorizePaymentMethod
throw new GenericPaymentDriverFailure('Error communicating with Authorize.net'); throw new GenericPaymentDriverFailure('Error communicating with Authorize.net');
} }
} }
public function deletePaymentProfile($gateway_customer_reference, $payment_profile_id) {
error_reporting(E_ALL & ~E_DEPRECATED);
$this->authorize->init();
// Set the transaction's refId
$refId = 'ref' . time();
// Use an existing payment profile ID for this Merchant name and Transaction key
$request = new DeleteCustomerPaymentProfileRequest();
$request->setMerchantAuthentication($this->authorize->merchant_authentication);
$request->setCustomerProfileId($gateway_customer_reference);
$request->setCustomerPaymentProfileId($payment_profile_id);
$controller = new DeleteCustomerPaymentProfileController($request);
$response = $controller->executeWithApiResponse($this->authorize->mode());
if (($response != null) && ($response->getMessages()->getResultCode() == "Ok") )
{
nlog("SUCCESS: Delete Customer Payment Profile SUCCESS :");
}
else
{
nlog("ERROR : Delete Customer Payment Profile: Invalid response\n");
$errorMessages = $response->getMessages()->getMessage();
nlog("Response : " . $errorMessages[0]->getCode() . " " .$errorMessages[0]->getText() . "\n");
}
return $response;
}
} }

View File

@ -156,6 +156,6 @@ class AuthorizePaymentDriver extends BaseDriver
*/ */
public function detach(ClientGatewayToken $token) public function detach(ClientGatewayToken $token)
{ {
// Authorize.net doesn't support this feature. return (new AuthorizePaymentMethod($this))->deletePaymentProfile($token->gateway_customer_reference, $token->token);
} }
} }

View File

@ -187,7 +187,7 @@ class BaseDriver extends AbstractPaymentDriver
$payment->invoices()->sync($invoices); $payment->invoices()->sync($invoices);
$invoices->each(function ($invoice) use ($payment) { $invoices->each(function ($invoice) use ($payment) {
event(new InvoiceWasPaid($invoice, $payment->company, Ninja::eventVars())); event(new InvoiceWasPaid($invoice, $payment, $payment->company, Ninja::eventVars()));
}); });
return $payment->service()->applyNumber()->save(); return $payment->service()->applyNumber()->save();

View File

@ -43,7 +43,9 @@ class CreditCard
*/ */
public function authorizeView($data) public function authorizeView($data)
{ {
return render('gateways.checkout.credit_card.authorize'); $data['gateway'] = $this->checkout;
return render('gateways.checkout.credit_card.authorize', $data);
} }
/** /**
@ -91,7 +93,7 @@ class CreditCard
$this->checkout->payment_hash->data = array_merge((array) $this->checkout->payment_hash->data, $state); $this->checkout->payment_hash->data = array_merge((array) $this->checkout->payment_hash->data, $state);
$this->checkout->payment_hash->save(); $this->checkout->payment_hash->save();
if ($request->has('token') && !is_null($request->token) && $request->pay_with_token) { if ($request->has('token') && !is_null($request->token) && !empty($request->token)) {
return $this->attemptPaymentUsingToken($request); return $this->attemptPaymentUsingToken($request);
} }

View File

@ -208,4 +208,9 @@ class CheckoutComPaymentDriver extends BaseDriver
return $this->processInternallyFailedPayment($this, $e); return $this->processInternallyFailedPayment($this, $e);
} }
} }
public function detach(ClientGatewayToken $clientGatewayToken)
{
// Gateway doesn't support this feature.
}
} }

View File

@ -39,6 +39,8 @@ class ACH
public function authorizeView(array $data) public function authorizeView(array $data)
{ {
$data['gateway'] = $this->stripe;
return render('gateways.stripe.ach.authorize', array_merge($data)); return render('gateways.stripe.ach.authorize', array_merge($data));
} }
@ -63,7 +65,12 @@ class ACH
public function verificationView(ClientGatewayToken $token) public function verificationView(ClientGatewayToken $token)
{ {
return render('gateways.stripe.ach.verify', compact('token')); $data = [
'token' => $token,
'gateway' => $this->stripe,
];
return render('gateways.stripe.ach.verify', $data);
} }
public function processVerification(Request $request, ClientGatewayToken $token) public function processVerification(Request $request, ClientGatewayToken $token)

View File

@ -66,13 +66,7 @@ class CreditCard
'description' => collect($data['invoices'])->pluck('id'), // TODO: More meaningful description. 'description' => collect($data['invoices'])->pluck('id'), // TODO: More meaningful description.
]; ];
if ($data['token']) { $payment_intent_data['setup_future_usage'] = 'off_session';
$payment_intent_data['payment_method'] = $data['token']->token;
} else {
$payment_intent_data['setup_future_usage'] = 'off_session';
// $payment_intent_data['save_payment_method'] = true;
// $payment_intent_data['confirm'] = true;
}
$data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data); $data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data);
$data['gateway'] = $this->stripe; $data['gateway'] = $this->stripe;

View File

@ -60,8 +60,8 @@ class ClientRepository extends BaseRepository
$client->fill($data); $client->fill($data);
if (!isset($client->id_number) || empty($client->id_number)) { if (!isset($client->number) || empty($client->number)) {
$client->id_number = $this->getNextClientNumber($client); $client->number = $this->getNextClientNumber($client);
} }
if (empty($data['name'])) { if (empty($data['name'])) {

View File

@ -117,6 +117,10 @@ class PaymentMigrationRepository extends BaseRepository
$inv->pivot->amount = $invoice_totals; $inv->pivot->amount = $invoice_totals;
$inv->pivot->refunded = $refund_totals; $inv->pivot->refunded = $refund_totals;
$inv->pivot->save(); $inv->pivot->save();
$inv->paid_to_date += $invoice_totals;
$inv->save();
}); });
} }
@ -130,6 +134,9 @@ class PaymentMigrationRepository extends BaseRepository
$payment->credits->each(function ($cre) use ($credit_totals) { $payment->credits->each(function ($cre) use ($credit_totals) {
$cre->pivot->amount = $credit_totals; $cre->pivot->amount = $credit_totals;
$cre->pivot->save(); $cre->pivot->save();
$cre->paid_to_date += $invoice_totals;
$cre->save();
}); });
} }

View File

@ -118,8 +118,6 @@ class PaymentRepository extends BaseRepository
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) { if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
$invoice_totals = array_sum(array_column($data['invoices'], 'amount')); $invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
nlog("invoice totals = {$invoice_totals}");
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get(); $invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
$payment->invoices()->saveMany($invoices); $payment->invoices()->saveMany($invoices);

View File

@ -47,8 +47,8 @@ class VendorRepository extends BaseRepository
$vendor->save(); $vendor->save();
if ($vendor->id_number == '' || ! $vendor->id_number) { if ($vendor->number == '' || ! $vendor->number) {
$vendor->id_number = $this->getNextVendorNumber($vendor); $vendor->number = $this->getNextVendorNumber($vendor);
} //todo write tests for this and make sure that custom vendor numbers also works as expected from here } //todo write tests for this and make sure that custom vendor numbers also works as expected from here
$vendor->save(); $vendor->save();

View File

@ -209,7 +209,7 @@ class PaymentMethod
$payment_urls = []; $payment_urls = [];
} }
$payment_urls[] = [ $this->payment_urls[] = [
'label' => ctrans('texts.apply_credit'), 'label' => ctrans('texts.apply_credit'),
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT, 'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
'gateway_type_id' => GatewayType::CREDIT, 'gateway_type_id' => GatewayType::CREDIT,
@ -238,6 +238,5 @@ class PaymentMethod
return true; return true;
} }
}
}

View File

@ -70,6 +70,7 @@ class ApplyPayment
} }
$this->credit->balance -= $this->amount_applied; $this->credit->balance -= $this->amount_applied;
$this->credit->paid_to_date += $this->amount_applied;
if ((int)$this->credit->balance == 0) { if ((int)$this->credit->balance == 0) {
$this->credit->status_id = Credit::STATUS_APPLIED; $this->credit->status_id = Credit::STATUS_APPLIED;
@ -110,6 +111,8 @@ class ApplyPayment
$this->payment->currency_id = $this->credit->client->getSetting('currency_id'); $this->payment->currency_id = $this->credit->client->getSetting('currency_id');
$this->payment->save(); $this->payment->save();
$this->payment->service()->applyNumber()->save();
$this->payment $this->payment
->invoices() ->invoices()
->attach($this->invoice->id, ['amount' => $this->amount_applied]); ->attach($this->invoice->id, ['amount' => $this->amount_applied]);
@ -133,6 +136,7 @@ class ApplyPayment
$this->invoice $this->invoice
->service() ->service()
->updateBalance($this->amount_applied * -1) ->updateBalance($this->amount_applied * -1)
->updatePaidToDate($this->amount_applied)
->updateStatus() ->updateStatus()
->save(); ->save();
@ -144,7 +148,7 @@ class ApplyPayment
if ((int)$this->invoice->balance == 0) { if ((int)$this->invoice->balance == 0) {
$this->invoice->service()->deletePdf(); $this->invoice->service()->deletePdf();
event(new InvoiceWasPaid($this->invoice, $this->payment->company, Ninja::eventVars())); event(new InvoiceWasPaid($this->invoice, $payment, $this->payment->company, Ninja::eventVars()));
} }
} }
} }

View File

@ -97,6 +97,12 @@ class CreditService
return $this; return $this;
} }
public function updatePaidToDate($adjustment)
{
$this->credit->paid_to_date += $adjustment;
return $this;
}
public function fillDefaults() public function fillDefaults()
{ {

View File

@ -104,14 +104,24 @@ class ApplyPayment extends AbstractService
->ledger() ->ledger()
->updatePaymentBalance($amount_paid); ->updatePaymentBalance($amount_paid);
$this->invoice->client->service()->updateBalance($amount_paid)->save(); $this->invoice
->client
->service()
->updateBalance($amount_paid)
->save();
/* Update Pivot Record amount */ /* Update Pivot Record amount */
$this->payment->invoices->each(function ($inv) use($amount_paid){ $this->payment->invoices->each(function ($inv) use($amount_paid){
if ($inv->id == $this->invoice->id) { if ($inv->id == $this->invoice->id) {
$inv->pivot->amount = ($amount_paid*-1); $inv->pivot->amount = ($amount_paid*-1);
$inv->pivot->save(); $inv->pivot->save();
$inv->paid_to_date += floatval($amount_paid*-1);
$inv->save();
} }
}); });
$this->invoice->service()->applyNumber()->save(); $this->invoice->service()->applyNumber()->save();

View File

@ -49,18 +49,12 @@ class AutoBillInvoice extends AbstractService
$this->invoice = $this->invoice->service()->markSent()->save(); $this->invoice = $this->invoice->service()->markSent()->save();
/* Mark the invoice as paid if there is no balance */ /* Mark the invoice as paid if there is no balance */
if ((int)$this->invoice->balance == 0) { if ((int)$this->invoice->balance == 0)
return $this->invoice->service()->markPaid()->save(); return $this->invoice->service()->markPaid()->save();
}
//if the credits cover the payments, we stop here, build the payment with credits and exit early //if the credits cover the payments, we stop here, build the payment with credits and exit early
if ($this->client->getSetting('use_credits_payment') != 'off')
if ($this->client->getSetting('use_credits_payment') != 'off') {
$this->applyCreditPayment(); $this->applyCreditPayment();
}
// info("partial = {$this->invoice->partial}");
// info("balance = {$this->invoice->balance}");
/* Determine $amount */ /* Determine $amount */
if ($this->invoice->partial > 0) { if ($this->invoice->partial > 0) {
@ -76,15 +70,12 @@ class AutoBillInvoice extends AbstractService
$gateway_token = $this->getGateway($amount); $gateway_token = $this->getGateway($amount);
/* Bail out if no payment methods available */ /* Bail out if no payment methods available */
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing) { if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing)
return $this->invoice; return $this->invoice;
}
/* $gateway fee */ /* $gateway fee */
$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes); $fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
//todo determine exact fee as per PaymentController
/* Build payment hash */ /* Build payment hash */
$payment_hash = PaymentHash::create([ $payment_hash = PaymentHash::create([
'hash' => Str::random(128), 'hash' => Str::random(128),
@ -122,18 +113,24 @@ class AutoBillInvoice extends AbstractService
$payment->invoices()->attach($this->invoice->id, ['amount' => $amount]); $payment->invoices()->attach($this->invoice->id, ['amount' => $amount]);
$this->invoice->service()->setStatus(Invoice::STATUS_PAID)->save(); $this->invoice
->service()
->setStatus(Invoice::STATUS_PAID)
->save();
foreach ($this->used_credit as $credit) { foreach ($this->used_credit as $credit) {
$current_credit = Credit::find($credit['credit_id']); $current_credit = Credit::find($credit['credit_id']);
$payment->credits()->attach($current_credit->id, ['amount' => $credit['amount']]); $payment->credits()
->attach($current_credit->id, ['amount' => $credit['amount']]);
info("adjusting credit balance {$current_credit->balance} by this amount ". $credit['amount']); info("adjusting credit balance {$current_credit->balance} by this amount ". $credit['amount']);
$current_credit->balance -= $credit['amount']; $current_credit->service()
->adjustBalance($credit['amount']*-1)
->updatePaidToDate($credit['amount'])
->setCalculatedStatus()
->save();
$current_credit->service()->setCalculatedStatus()->save();
// $this->applyPaymentToCredit($current_credit, $credit['amount']);
} }
$payment->ledger() $payment->ledger()
@ -153,7 +150,10 @@ class AutoBillInvoice extends AbstractService
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
return $this->invoice->service()->setCalculatedStatus()->save(); return $this->invoice
->service()
->setCalculatedStatus()
->save();
} }
/** /**
@ -190,10 +190,11 @@ class AutoBillInvoice extends AbstractService
if ($is_partial_amount) { if ($is_partial_amount) {
//more credit than needed //more credit than needed
if ($credit->balance >= $this->invoice->partial) { if ($credit->balance > $this->invoice->partial) {
$this->used_credit[$key]['credit_id'] = $credit->id; $this->used_credit[$key]['credit_id'] = $credit->id;
$this->used_credit[$key]['amount'] = $this->invoice->partial; $this->used_credit[$key]['amount'] = $this->invoice->partial;
$this->invoice->balance -= $this->invoice->partial; $this->invoice->balance -= $this->invoice->partial;
$this->invoice->paid_to_date += $this->invoice->partial;
$this->invoice->partial = 0; $this->invoice->partial = 0;
break; break;
} else { } else {
@ -201,19 +202,23 @@ class AutoBillInvoice extends AbstractService
$this->used_credit[$key]['amount'] = $credit->balance; $this->used_credit[$key]['amount'] = $credit->balance;
$this->invoice->partial -= $credit->balance; $this->invoice->partial -= $credit->balance;
$this->invoice->balance -= $credit->balance; $this->invoice->balance -= $credit->balance;
$this->invoice->paid_to_date += $credit->balance;
} }
} else { } else {
//more credit than needed //more credit than needed
if ($credit->balance >= $this->invoice->balance) { if ($credit->balance > $this->invoice->balance) {
$this->used_credit[$key]['credit_id'] = $credit->id; $this->used_credit[$key]['credit_id'] = $credit->id;
$this->used_credit[$key]['amount'] = $this->invoice->balance; $this->used_credit[$key]['amount'] = $this->invoice->balance;
$this->invoice->paid_to_date += $this->invoice->balance;
$this->invoice->balance = 0; $this->invoice->balance = 0;
break; break;
} else { } else {
$this->used_credit[$key]['credit_id'] = $credit->id; $this->used_credit[$key]['credit_id'] = $credit->id;
$this->used_credit[$key]['amount'] = $credit->balance; $this->used_credit[$key]['amount'] = $credit->balance;
$this->invoice->balance -= $credit->balance; $this->invoice->balance -= $credit->balance;
$this->invoice->paid_to_date += $credit->balance;
} }
} }
} }

View File

@ -38,14 +38,12 @@ class HandleReversal extends AbstractService
public function run() public function run()
{ {
/* Check again!! */ /* Check again!! */
if (! $this->invoice->invoiceReversable($this->invoice)) { if (! $this->invoice->invoiceReversable($this->invoice))
return $this->invoice; return $this->invoice;
}
/* If the invoice has been cancelled - we need to unwind the cancellation before reversing*/ /* If the invoice has been cancelled - we need to unwind the cancellation before reversing*/
if ($this->invoice->status_id == Invoice::STATUS_CANCELLED) { if ($this->invoice->status_id == Invoice::STATUS_CANCELLED)
$this->invoice = $this->invoice->service()->reverseCancellation()->save(); $this->invoice = $this->invoice->service()->reverseCancellation()->save();
}
$balance_remaining = $this->invoice->balance; $balance_remaining = $this->invoice->balance;
@ -57,18 +55,19 @@ class HandleReversal extends AbstractService
->get(); ->get();
$paymentables->each(function ($paymentable) use ($total_paid) { $paymentables->each(function ($paymentable) use ($total_paid) {
$reversable_amount = $paymentable->amount - $paymentable->refunded; $reversable_amount = $paymentable->amount - $paymentable->refunded;
$total_paid -= $reversable_amount; $total_paid -= $reversable_amount;
$paymentable->amount = $paymentable->refunded; $paymentable->amount = $paymentable->refunded;
$paymentable->save(); $paymentable->save();
}); });
/* Generate a credit for the $total_paid amount */ /* Generate a credit for the $total_paid amount */
$notes = 'Credit for reversal of '.$this->invoice->number; $notes = 'Credit for reversal of '.$this->invoice->number;
if ($total_paid > 0) { if ($total_paid > 0) {
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id); $credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
$credit->client_id = $this->invoice->client_id; $credit->client_id = $this->invoice->client_id;
$credit->invoice_id = $this->invoice->id; $credit->invoice_id = $this->invoice->id;
@ -79,16 +78,13 @@ class HandleReversal extends AbstractService
$item->notes = $notes; $item->notes = $notes;
$line_items[] = $item; $line_items[] = $item;
$credit->line_items = $line_items; $credit->line_items = $line_items;
$credit->save(); $credit->save();
$credit_calc = new InvoiceSum($credit); $credit_calc = new InvoiceSum($credit);
$credit_calc->build(); $credit_calc->build();
$credit = $credit_calc->purgeTaxes()->getCredit(); $credit = $credit_calc->purgeTaxes()->getCredit();
$credit->service()->markSent()->save(); $credit->service()->markSent()->save();
} }
@ -105,6 +101,9 @@ class HandleReversal extends AbstractService
//harvest the credit record and add in the amount for the credit. //harvest the credit record and add in the amount for the credit.
$paymentable_credit->pivot->amount = $total_paid; $paymentable_credit->pivot->amount = $total_paid;
$paymentable_credit->pivot->save(); $paymentable_credit->pivot->save();
$paymentable_credit->paid_to_date += $total_paid;
$paymentable_credit->save();
} }
/* Set invoice balance to 0 */ /* Set invoice balance to 0 */
@ -113,7 +112,8 @@ class HandleReversal extends AbstractService
} }
$this->invoice->balance = 0; $this->invoice->balance = 0;
$this->invoice->paid_to_date = 0;
/* Set invoice status to reversed... somehow*/ /* Set invoice status to reversed... somehow*/
$this->invoice->service()->setStatus(Invoice::STATUS_REVERSED)->save(); $this->invoice->service()->setStatus(Invoice::STATUS_REVERSED)->save();

View File

@ -112,6 +112,13 @@ class InvoiceService
return $this; return $this;
} }
public function updatePaidToDate($adjustment)
{
$this->invoice->paid_to_date += $adjustment;
return $this;
}
public function createInvitations() public function createInvitations()
{ {
$this->invoice = (new CreateInvitations($this->invoice))->run(); $this->invoice = (new CreateInvitations($this->invoice))->run();

View File

@ -70,6 +70,7 @@ class MarkPaid extends AbstractService
$this->invoice->service() $this->invoice->service()
->updateBalance($payment->amount * -1) ->updateBalance($payment->amount * -1)
->updatePaidToDate($payment->amount)
->setStatus(Invoice::STATUS_PAID) ->setStatus(Invoice::STATUS_PAID)
->applyNumber() ->applyNumber()
->save(); ->save();
@ -79,7 +80,7 @@ class MarkPaid extends AbstractService
/* Update Invoice balance */ /* Update Invoice balance */
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
event(new InvoiceWasPaid($this->invoice, $payment->company, Ninja::eventVars())); event(new InvoiceWasPaid($this->invoice, $payment, $payment->company, Ninja::eventVars()));
$payment->ledger() $payment->ledger()
->updatePaymentBalance($payment->amount * -1); ->updatePaymentBalance($payment->amount * -1);

View File

@ -33,7 +33,7 @@ class UpdateBalance extends AbstractService
} }
$this->invoice->balance += floatval($this->balance_adjustment); $this->invoice->balance += floatval($this->balance_adjustment);
if ($this->invoice->balance == 0) { if ($this->invoice->balance == 0) {
$this->invoice->status_id = Invoice::STATUS_PAID; $this->invoice->status_id = Invoice::STATUS_PAID;
} }

View File

@ -55,7 +55,6 @@ class DeletePayment
private function cleanupPayment() private function cleanupPayment()
{ {
$this->payment->is_deleted = true; $this->payment->is_deleted = true;
// $entity->save();
$this->payment->delete(); $this->payment->delete();
return $this; return $this;
@ -78,10 +77,22 @@ class DeletePayment
private function adjustInvoices() private function adjustInvoices()
{ {
if ($this->payment->invoices()->exists()) { if ($this->payment->invoices()->exists()) {
$this->payment->invoices()->each(function ($paymentable_invoice) { $this->payment->invoices()->each(function ($paymentable_invoice) {
$paymentable_invoice->service()->updateBalance($paymentable_invoice->pivot->amount)->save();
$paymentable_invoice->ledger()->updateInvoiceBalance($paymentable_invoice->pivot->amount, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")->save(); $paymentable_invoice->service()
$paymentable_invoice->client->service()->updateBalance($paymentable_invoice->pivot->amount)->save(); ->updateBalance($paymentable_invoice->pivot->amount)
->updatePaidToDate($paymentable_invoice->pivot->amount * -1)
->save();
$paymentable_invoice->ledger()
->updateInvoiceBalance($paymentable_invoice->pivot->amount, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")
->save();
$paymentable_invoice->client
->service()
->updateBalance($paymentable_invoice->pivot->amount)
->save();
if ($paymentable_invoice->balance == $paymentable_invoice->amount) { if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save(); $paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
@ -101,10 +112,12 @@ class DeletePayment
{ {
if ($this->payment->credits()->exists()) { if ($this->payment->credits()->exists()) {
$this->payment->credits()->each(function ($paymentable_credit) { $this->payment->credits()->each(function ($paymentable_credit) {
$paymentable_credit->balance += $paymentable_credit->pivot->amount;
$paymentable_credit->setStatus(Credit::STATUS_SENT); $paymentable_credit->service()
//fire event for this credit ->updateBalance($paymentable_credit->pivot->amount)
// ->updatePaidToDate($paymentable_credit->pivot->amount*-1)
->setStatus(Credit::STATUS_SENT)
->save();
}); });
} }

View File

@ -59,11 +59,16 @@ class PaymentService
$client = $this->payment->client; $client = $this->payment->client;
$invoices->each(function ($invoice) { $invoices->each(function ($invoice) {
if ($invoice->pivot->amount > 0) { if ($invoice->pivot->amount > 0) {
$invoice->status_id = Invoice::STATUS_SENT;
$invoice->balance = $invoice->pivot->amount; $invoice->service()
$invoice->save(); ->updateBalance($invoice->pivot->amount)
->updatePaidToDate($invoice->pivot->amount*-1)
->setStatus(Invoice::STATUS_SENT)
->save();
} }
}); });
$this->payment $this->payment

View File

@ -188,18 +188,26 @@ class RefundPayment
if ($available_credit > $this->total_refund) { if ($available_credit > $this->total_refund) {
$paymentable_credit->pivot->refunded += $this->total_refund; $paymentable_credit->pivot->refunded += $this->total_refund;
$paymentable_credit->pivot->save(); $paymentable_credit->pivot->save();
$paymentable_credit->balance += $this->total_refund;
$paymentable_credit->service()->setStatus(Credit::STATUS_SENT)->save(); $paymentable_credit->service()
//$paymentable_credit->save(); ->setStatus(Credit::STATUS_SENT)
->updateBalance($this->total_refund)
->updatePaidToDate($this->total_refund*-1)
->save();
$this->total_refund = 0; $this->total_refund = 0;
} else { } else {
$paymentable_credit->pivot->refunded += $available_credit; $paymentable_credit->pivot->refunded += $available_credit;
$paymentable_credit->pivot->save(); $paymentable_credit->pivot->save();
$paymentable_credit->balance += $available_credit; $paymentable_credit->balance += $available_credit;
$paymentable_credit->service()->setStatus(Credit::STATUS_SENT)->save(); $paymentable_credit->service()
// $paymentable_credit->save(); ->setStatus(Credit::STATUS_SENT)
->adjustBalance($available_credit)
->updatePaidToDate($available_credit*-1)
->save();
$this->total_refund -= $available_credit; $this->total_refund -= $available_credit;
} }

View File

@ -79,6 +79,7 @@ class UpdateInvoicePayment
$invoice->service() //caution what if we amount paid was less than partial - we wipe it! $invoice->service() //caution what if we amount paid was less than partial - we wipe it!
->clearPartial() ->clearPartial()
->updateBalance($paid_amount * -1) ->updateBalance($paid_amount * -1)
->updatePaidToDate($paid_amount)
->updateStatus() ->updateStatus()
->save(); ->save();

View File

@ -41,6 +41,7 @@ class CreateInvitations
$invitation = QuoteInvitation::whereCompanyId($this->quote->company_id) $invitation = QuoteInvitation::whereCompanyId($this->quote->company_id)
->whereClientContactId($contact->id) ->whereClientContactId($contact->id)
->whereQuoteId($this->quote->id) ->whereQuoteId($this->quote->id)
->withTrashed()
->first(); ->first();
if (! $invitation && $contact->send_email) { if (! $invitation && $contact->send_email) {

View File

@ -147,6 +147,7 @@ class ClientTransformer extends EntityTransformer
'archived_at' => (int) $client->deleted_at, 'archived_at' => (int) $client->deleted_at,
'created_at' => (int) $client->created_at, 'created_at' => (int) $client->created_at,
'display_name' => $client->present()->name(), 'display_name' => $client->present()->name(),
'number' => (string) $client->number ?: '',
]; ];
} }
} }

View File

@ -136,6 +136,7 @@ class CreditTransformer extends EntityTransformer
'line_items' => $credit->line_items ?: (array) [], 'line_items' => $credit->line_items ?: (array) [],
'entity_type' => 'credit', 'entity_type' => 'credit',
'exchange_rate' => (float) $credit->exchange_rate, 'exchange_rate' => (float) $credit->exchange_rate,
'paid_to_date' => (float) $credit->paid_to_date,
]; ];
} }
} }

View File

@ -140,6 +140,8 @@ class InvoiceTransformer extends EntityTransformer
'reminder2_sent' => $invoice->reminder2_sent ?: '', 'reminder2_sent' => $invoice->reminder2_sent ?: '',
'reminder3_sent' => $invoice->reminder3_sent ?: '', 'reminder3_sent' => $invoice->reminder3_sent ?: '',
'reminder_last_sent' => $invoice->reminder_last_sent ?: '', 'reminder_last_sent' => $invoice->reminder_last_sent ?: '',
'paid_to_date' => (float) $invoice->paid_to_date,
]; ];
} }
} }

View File

@ -137,6 +137,7 @@ class QuoteTransformer extends EntityTransformer
'line_items' => $quote->line_items ?: (array) [], 'line_items' => $quote->line_items ?: (array) [],
'entity_type' => 'quote', 'entity_type' => 'quote',
'exchange_rate' => (float) $quote->exchange_rate, 'exchange_rate' => (float) $quote->exchange_rate,
'paid_to_date' => (float) $quote->paid_to_date,
'project_id' => $this->encodePrimaryKey($quote->project_id), 'project_id' => $this->encodePrimaryKey($quote->project_id),
]; ];
} }

View File

@ -138,6 +138,7 @@ class RecurringInvoiceTransformer extends EntityTransformer
'auto_bill' => (string) $invoice->auto_bill, 'auto_bill' => (string) $invoice->auto_bill,
'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled, 'auto_bill_enabled' => (bool) $invoice->auto_bill_enabled,
'due_date_days' => (string) $invoice->due_date_days ?: '', 'due_date_days' => (string) $invoice->due_date_days ?: '',
'paid_to_date' => (float) $invoice->paid_to_date,
]; ];
} }
} }

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