diff --git a/VERSION.txt b/VERSION.txt
index 96cf4cd38b12..75d1c3cf3216 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-5.5.89
\ No newline at end of file
+5.5.90
\ No newline at end of file
diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php
index 01a5e9e06e7e..d2548e27e414 100644
--- a/app/Console/Commands/CheckData.php
+++ b/app/Console/Commands/CheckData.php
@@ -12,35 +12,36 @@
namespace App\Console\Commands;
use App;
+use Exception;
+use App\Models\User;
+use App\Utils\Ninja;
+use App\Models\Quote;
+use App\Models\Client;
+use App\Models\Credit;
+use App\Models\Vendor;
+use App\Models\Account;
+use App\Models\Company;
+use App\Models\Contact;
+use App\Models\Invoice;
+use App\Models\Payment;
+use App\Models\CompanyUser;
+use Illuminate\Support\Str;
+use App\Models\CompanyToken;
+use App\Models\ClientContact;
+use App\Models\CompanyLedger;
+use App\Models\PurchaseOrder;
+use App\Models\VendorContact;
+use App\Models\QuoteInvitation;
+use Illuminate\Console\Command;
+use App\Models\CreditInvitation;
+use App\Models\InvoiceInvitation;
use App\DataMapper\ClientSettings;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Mail;
use App\Factory\ClientContactFactory;
use App\Factory\VendorContactFactory;
use App\Jobs\Company\CreateCompanyToken;
-use App\Models\Account;
-use App\Models\Client;
-use App\Models\ClientContact;
-use App\Models\Company;
-use App\Models\CompanyLedger;
-use App\Models\CompanyUser;
-use App\Models\Contact;
-use App\Models\Credit;
-use App\Models\CreditInvitation;
-use App\Models\Invoice;
-use App\Models\InvoiceInvitation;
-use App\Models\Payment;
-use App\Models\PurchaseOrder;
-use App\Models\Quote;
-use App\Models\QuoteInvitation;
use App\Models\RecurringInvoiceInvitation;
-use App\Models\User;
-use App\Models\Vendor;
-use App\Models\VendorContact;
-use App\Utils\Ninja;
-use Exception;
-use Illuminate\Console\Command;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Mail;
-use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputOption;
/*
@@ -160,16 +161,33 @@ class CheckData extends Command
private function checkCompanyTokens()
{
- CompanyUser::doesnthave('token')->cursor()->each(function ($cu) {
- if ($cu->user) {
+ // CompanyUser::whereDoesntHave('token', function ($query){
+ // return $query->where('is_system', 1);
+ // })->cursor()->each(function ($cu){
+ // if ($cu->user) {
+ // $this->logMessage("Creating missing company token for user # {$cu->user->id} for company id # {$cu->company->id}");
+ // (new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
+ // } else {
+ // $this->logMessage("Dangling User ID # {$cu->id}");
+ // }
+ // });
+
+ CompanyUser::query()->cursor()->each(function ($cu) {
+ if (CompanyToken::where('user_id', $cu->user_id)->where('company_id', $cu->company_id)->where('is_system', 1)->doesntExist()) {
$this->logMessage("Creating missing company token for user # {$cu->user->id} for company id # {$cu->company->id}");
- (new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
- } else {
- $this->logMessage("Dangling User ID # {$cu->id}");
+ (new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
}
});
- }
+
+
+ }
+
+ /**
+ * checkOauthSanity
+ *
+ * @return void
+ */
private function checkOauthSanity()
{
User::where('oauth_provider_id', '1')->cursor()->each(function ($user) {
diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php
index b5aa38a104b9..d979030514fd 100644
--- a/app/DataMapper/CompanySettings.php
+++ b/app/DataMapper/CompanySettings.php
@@ -467,7 +467,16 @@ class CompanySettings extends BaseSettings
public $show_task_item_description = false;
+ public $client_initiated_payments = false;
+
+ public $client_initiated_payments_minimum = 0;
+
+ public $sync_invoice_quote_columns = true;
+
public static $casts = [
+ 'client_initiated_payments' => 'bool',
+ 'client_initiated_payments_minimum' => 'float',
+ 'sync_invoice_quote_columns' => 'bool',
'show_task_item_description' => 'bool',
'allow_billable_task_items' => 'bool',
'accept_client_input_quote_approval' => 'bool',
@@ -907,6 +916,15 @@ class CompanySettings extends BaseSettings
'$product.tax',
'$product.line_total',
],
+ 'product_quote_columns' => [
+ '$product.item',
+ '$product.description',
+ '$product.unit_cost',
+ '$product.quantity',
+ '$product.discount',
+ '$product.tax',
+ '$product.line_total',
+ ],
'task_columns' =>[
'$task.service',
'$task.description',
diff --git a/app/Filters/CreditFilters.php b/app/Filters/CreditFilters.php
index 7fa39b281c21..7f00792adc0f 100644
--- a/app/Filters/CreditFilters.php
+++ b/app/Filters/CreditFilters.php
@@ -85,7 +85,10 @@ class CreditFilters extends QueryFilters
->orWhere('credits.custom_value1', 'like', '%'.$filter.'%')
->orWhere('credits.custom_value2', 'like', '%'.$filter.'%')
->orWhere('credits.custom_value3', 'like', '%'.$filter.'%')
- ->orWhere('credits.custom_value4', 'like', '%'.$filter.'%');
+ ->orWhere('credits.custom_value4', 'like', '%'.$filter.'%')
+ ->orWhereHas('client', function ($q) use ($filter){
+ $q->where('name', 'like', '%'.$filter.'%');
+ });
});
}
diff --git a/app/Filters/InvoiceFilters.php b/app/Filters/InvoiceFilters.php
index 6ae3da8e6f66..81f3e6db68dd 100644
--- a/app/Filters/InvoiceFilters.php
+++ b/app/Filters/InvoiceFilters.php
@@ -69,6 +69,7 @@ class InvoiceFilters extends QueryFilters
if (in_array('overdue', $status_parameters)) {
$query->orWhereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('due_date', '<', Carbon::now())
+ ->orWhere('due_date', null)
->orWhere('partial_due_date', '<', Carbon::now());
}
});
@@ -107,7 +108,10 @@ class InvoiceFilters extends QueryFilters
->orWhere('custom_value1', 'like', '%'.$filter.'%')
->orWhere('custom_value2', 'like', '%'.$filter.'%')
->orWhere('custom_value3', 'like', '%'.$filter.'%')
- ->orWhere('custom_value4', 'like', '%'.$filter.'%');
+ ->orWhere('custom_value4', 'like', '%'.$filter.'%')
+ ->orWhereHas('client', function ($q) use ($filter){
+ $q->where('name', 'like', '%'.$filter.'%');
+ });
});
}
diff --git a/app/Filters/PurchaseOrderFilters.php b/app/Filters/PurchaseOrderFilters.php
index a3ef1709c6f4..61fdbb9956fc 100644
--- a/app/Filters/PurchaseOrderFilters.php
+++ b/app/Filters/PurchaseOrderFilters.php
@@ -93,7 +93,10 @@ class PurchaseOrderFilters extends QueryFilters
->orWhere('custom_value1', 'like', '%'.$filter.'%')
->orWhere('custom_value2', 'like', '%'.$filter.'%')
->orWhere('custom_value3', 'like', '%'.$filter.'%')
- ->orWhere('custom_value4', 'like', '%'.$filter.'%');
+ ->orWhere('custom_value4', 'like', '%'.$filter.'%')
+ ->orWhereHas('vendor', function ($q) use ($filter){
+ $q->where('name', 'like', '%'.$filter.'%');
+ });
});
}
diff --git a/app/Filters/QuoteFilters.php b/app/Filters/QuoteFilters.php
index ca011386d944..7b4151b7983a 100644
--- a/app/Filters/QuoteFilters.php
+++ b/app/Filters/QuoteFilters.php
@@ -37,7 +37,10 @@ class QuoteFilters extends QueryFilters
->orwhere('custom_value1', 'like', '%'.$filter.'%')
->orWhere('custom_value2', 'like', '%'.$filter.'%')
->orWhere('custom_value3', 'like', '%'.$filter.'%')
- ->orWhere('custom_value4', 'like', '%'.$filter.'%');
+ ->orWhere('custom_value4', 'like', '%'.$filter.'%')
+ ->orWhereHas('client', function ($q) use ($filter){
+ $q->where('name', 'like', '%'.$filter.'%');
+ });
});
}
diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php
index 6afe275e37b0..268b54e71d9d 100644
--- a/app/Http/Controllers/AccountController.php
+++ b/app/Http/Controllers/AccountController.php
@@ -154,7 +154,7 @@ class AccountController extends BaseController
$truth->setUser(auth()->user());
$truth->setCompany($ct->first()->company);
- return $this->listResponse($ct);
+ return $this->listResponse($ct->fresh());
}
public function update(UpdateAccountRequest $request, Account $account)
diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php
index 5d4f4b347947..413bf66a3118 100644
--- a/app/Http/Controllers/Auth/LoginController.php
+++ b/app/Http/Controllers/Auth/LoginController.php
@@ -1,4 +1,5 @@
6 characters",
- * type="string"
- * )
- * )
- * )
- * ),
- * @OA\Response(
- * response=200,
- * description="The Company User response",
- * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
- * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
- * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
- * @OA\JsonContent(ref="#/components/schemas/CompanyUser"),
- * ),
- * @OA\Response(
- * response=422,
- * description="Validation error",
- * @OA\JsonContent(ref="#/components/schemas/ValidationError"),
- * ),
- * @OA\Response(
- * response="default",
- * description="Unexpected Error",
- * @OA\JsonContent(ref="#/components/schemas/Error"),
- * ),
- * )
*/
public function apiLogin(LoginRequest $request)
{
@@ -175,7 +106,7 @@ class LoginController extends BaseController
if ($this->attemptLogin($request)) {
LightLogs::create(new LoginSuccess())
->increment()
- ->queue();
+ ->batch();
$user = $this->guard()->user();
@@ -221,7 +152,7 @@ class LoginController extends BaseController
} else {
LightLogs::create(new LoginFailure())
->increment()
- ->queue();
+ ->batch();
$this->incrementLoginAttempts($request);
@@ -236,39 +167,7 @@ class LoginController extends BaseController
* Refreshes the data feed with the current Company User.
*
* @param Request $request
- * @return CompanyUser Refresh Feed.
- *
- *
- * @OA\Post(
- * path="/api/v1/refresh",
- * operationId="refresh",
- * tags={"refresh"},
- * summary="Refreshes the dataset",
- * description="Refreshes the dataset",
- * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
- * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
- * @OA\Parameter(ref="#/components/parameters/include"),
- * @OA\Parameter(ref="#/components/parameters/include_static"),
- * @OA\Parameter(ref="#/components/parameters/clear_cache"),
- * @OA\Response(
- * response=200,
- * description="The Company User response",
- * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
- * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
- * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
- * @OA\JsonContent(ref="#/components/schemas/CompanyUser"),
- * ),
- * @OA\Response(
- * response=422,
- * description="Validation error",
- * @OA\JsonContent(ref="#/components/schemas/ValidationError"),
- * ),
- * @OA\Response(
- * response="default",
- * description="Unexpected Error",
- * @OA\JsonContent(ref="#/components/schemas/Error"),
- * ),
- * )
+ * @return CompanyUser Refresh Feed.
*/
public function refresh(Request $request)
{
@@ -346,7 +245,7 @@ class LoginController extends BaseController
private function handleSocialiteLogin($provider, $token)
{
$user = $this->getSocialiteUser($provider, $token);
-
+
if ($user) {
return $this->loginOrCreateFromSocialite($user, $provider);
}
@@ -363,7 +262,7 @@ class LoginController extends BaseController
'oauth_user_id' => $user->id,
'oauth_provider_id' => $provider,
];
-
+
if ($existing_user = MultiDB::hasUser($query)) {
if (!$existing_user->account) {
return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
@@ -408,7 +307,7 @@ class LoginController extends BaseController
return $this->timeConstrainedResponse($cu);
}
-
+
nlog("socialite");
nlog($user);
@@ -478,7 +377,7 @@ class LoginController extends BaseController
if (auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) {
auth()->user()->companies->each(function ($company) {
- if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()) {
+ if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->where('is_system',true)->exists()) {
(new CreateCompanyToken($company, auth()->user(), 'Google_O_Auth'))->handle();
}
});
@@ -499,7 +398,6 @@ class LoginController extends BaseController
return response()->json(['message' => 'Invalid response from oauth server, no access token in response.'], 400);
}
-
$graph = new \Microsoft\Graph\Graph();
$graph->setAccessToken($accessToken);
@@ -536,17 +434,22 @@ class LoginController extends BaseController
return $this->existingLoginUser($user->getId(), 'microsoft');
}
- // Signup!
- $new_account = [
- 'first_name' => $user->getGivenName() ?: '',
- 'last_name' => $user->getSurname() ?: '',
- 'password' => '',
- 'email' => $email,
- 'oauth_user_id' => $user->getId(),
- 'oauth_provider_id' => 'microsoft',
- ];
- return $this->createNewAccount($new_account);
+ // Signup!
+ if (request()->has('create') && request()->input('create') == 'true') {
+ $new_account = [
+ 'first_name' => $user->getGivenName() ?: '',
+ 'last_name' => $user->getSurname() ?: '',
+ 'password' => '',
+ 'email' => $email,
+ 'oauth_user_id' => $user->getId(),
+ 'oauth_provider_id' => 'microsoft',
+ ];
+
+ return $this->createNewAccount($new_account);
+ }
+
+ return response()->json(['message' => 'User not found. If you believe this is an error, please send an email to contact@invoiceninja.com'], 400);
}
@@ -640,19 +543,23 @@ class LoginController extends BaseController
return $this->existingLoginUser($google->harvestSubField($user), 'google');
}
- //user not found anywhere - lets sign them up.
- $name = OAuth::splitName($google->harvestName($user));
+ if (request()->has('create') && request()->input('create') == 'true') {
+ //user not found anywhere - lets sign them up.
+ $name = OAuth::splitName($google->harvestName($user));
- $new_account = [
- 'first_name' => $name[0],
- 'last_name' => $name[1],
- 'password' => '',
- 'email' => $google->harvestEmail($user),
- 'oauth_user_id' => $google->harvestSubField($user),
- 'oauth_provider_id' => 'google',
- ];
+ $new_account = [
+ 'first_name' => $name[0],
+ 'last_name' => $name[1],
+ 'password' => '',
+ 'email' => $google->harvestEmail($user),
+ 'oauth_user_id' => $google->harvestSubField($user),
+ 'oauth_provider_id' => 'google',
+ ];
- return $this->createNewAccount($new_account);
+ return $this->createNewAccount($new_account);
+ }
+
+ return response()->json(['message' => 'User not found. If you believe this is an error, please send an email to contact@invoiceninja.com'], 400);
}
return response()
@@ -700,7 +607,7 @@ class LoginController extends BaseController
if ($provider == 'microsoft') {
$scopes = ['email', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid'];
- $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url')."/auth/microsoft"];
+ $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"];
}
if (request()->has('code')) {
diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php
deleted file mode 100644
index 2fc7685c525c..000000000000
--- a/app/Http/Controllers/Auth/RegisterController.php
+++ /dev/null
@@ -1,81 +0,0 @@
-middleware('guest');
- }
-
- /**
- * Get a validator for an incoming registration request.
- *
- * @param array $data
- * @return \Illuminate\Contracts\Validation\Validator
- */
- protected function validator(array $data)
- {
- return Validator::make($data, [
- 'first_name' => 'required|string|max:255',
- 'email' => 'required|string|email|max:255|unique:users',
- 'password' => 'required|string|min:6|confirmed',
- ]);
- }
-
- /**
- * Create a new user instance after a valid registration.
- *
- * @param array $data
- * @return \App\User
- */
- protected function create(array $data)
- {
- return User::create([
- 'first_name' => $data['first_name'],
- 'email' => $data['email'],
- 'password' => Hash::make($data['password']),
- ]);
- }
-}
diff --git a/app/Http/Controllers/Auth/VendorContactLoginController.php b/app/Http/Controllers/Auth/VendorContactLoginController.php
index 91708e065faf..6a50083bbace 100644
--- a/app/Http/Controllers/Auth/VendorContactLoginController.php
+++ b/app/Http/Controllers/Auth/VendorContactLoginController.php
@@ -28,16 +28,13 @@ class VendorContactLoginController extends Controller
public function catch()
{
- $data = [
-
- ];
-
return $this->render('purchase_orders.catch');
}
public function logout()
{
Auth::guard('vendor')->logout();
+
request()->session()->invalidate();
return redirect('/vendors');
diff --git a/app/Http/Controllers/Auth/VerificationController.php b/app/Http/Controllers/Auth/VerificationController.php
deleted file mode 100644
index 33f9856001ae..000000000000
--- a/app/Http/Controllers/Auth/VerificationController.php
+++ /dev/null
@@ -1,50 +0,0 @@
-middleware('auth');
- $this->middleware('signed')->only('verify');
- $this->middleware('throttle:6,1')->only('verify', 'resend');
- }
-}
diff --git a/app/Http/Controllers/EmailController.php b/app/Http/Controllers/EmailController.php
index 56d76a4c79d8..af73ff04ccff 100644
--- a/app/Http/Controllers/EmailController.php
+++ b/app/Http/Controllers/EmailController.php
@@ -31,6 +31,7 @@ use App\Http\Requests\Email\SendEmailRequest;
use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
use App\Transformers\PurchaseOrderTransformer;
use App\Transformers\RecurringInvoiceTransformer;
+use Illuminate\Mail\Mailables\Address;
class EmailController extends BaseController
{
@@ -135,6 +136,8 @@ class EmailController extends BaseController
$mo->email_template_body = $request->input('template');
$mo->email_template_subject = str_replace("template", "subject", $request->input('template'));
+ if($request->has('cc_email'))
+ $mo->cc[] = new Address($request->cc_email);
// if ($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder') {
// return $this->sendPurchaseOrder($entity_obj, $data, $template);
diff --git a/app/Http/Controllers/RecurringInvoiceController.php b/app/Http/Controllers/RecurringInvoiceController.php
index d19830888da4..77078d3a818c 100644
--- a/app/Http/Controllers/RecurringInvoiceController.php
+++ b/app/Http/Controllers/RecurringInvoiceController.php
@@ -11,27 +11,29 @@
namespace App\Http\Controllers;
-use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
-use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated;
+use App\Utils\Ninja;
+use App\Models\Account;
+use Illuminate\Http\Response;
+use App\Utils\Traits\MakesHash;
+use App\Models\RecurringInvoice;
+use App\Utils\Traits\SavesDocuments;
+use Illuminate\Support\Facades\Storage;
use App\Factory\RecurringInvoiceFactory;
use App\Filters\RecurringInvoiceFilters;
-use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest;
-use App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest;
-use App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest;
+use App\Jobs\RecurringInvoice\UpdateRecurring;
+use App\Repositories\RecurringInvoiceRepository;
+use App\Transformers\RecurringInvoiceTransformer;
+use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
+use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated;
+use App\Http\Requests\RecurringInvoice\BulkRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\EditRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest;
+use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest;
+use App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\UploadRecurringInvoiceRequest;
-use App\Models\Account;
-use App\Models\RecurringInvoice;
-use App\Repositories\RecurringInvoiceRepository;
-use App\Transformers\RecurringInvoiceTransformer;
-use App\Utils\Ninja;
-use App\Utils\Traits\MakesHash;
-use App\Utils\Traits\SavesDocuments;
-use Illuminate\Http\Response;
-use Illuminate\Support\Facades\Storage;
+use App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest;
/**
* Class RecurringInvoiceController.
@@ -392,50 +394,6 @@ class RecurringInvoiceController extends BaseController
*
* @param DestroyRecurringInvoiceRequest $request
* @param RecurringInvoice $recurring_invoice
- *
- * @return Response
- *
- *
- * @throws \Exception
- * @OA\Delete(
- * path="/api/v1/recurring_invoices/{id}",
- * operationId="deleteRecurringInvoice",
- * tags={"recurring_invoices"},
- * summary="Deletes a RecurringInvoice",
- * description="Handles the deletion of an RecurringInvoice by id",
- * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
- * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
- * @OA\Parameter(ref="#/components/parameters/include"),
- * @OA\Parameter(
- * name="id",
- * in="path",
- * description="The RecurringInvoice Hashed ID",
- * example="D2J234DFA",
- * required=true,
- * @OA\Schema(
- * type="string",
- * format="string",
- * ),
- * ),
- * @OA\Response(
- * response=200,
- * description="Returns a HTTP status",
- * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
- * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
- * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
- * ),
- * @OA\Response(
- * response=422,
- * description="Validation error",
- * @OA\JsonContent(ref="#/components/schemas/ValidationError"),
- *
- * ),
- * @OA\Response(
- * response="default",
- * description="Unexpected Error",
- * @OA\JsonContent(ref="#/components/schemas/Error"),
- * ),
- * )
*/
public function destroy(DestroyRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice)
{
@@ -445,195 +403,31 @@ class RecurringInvoiceController extends BaseController
}
/**
- * @OA\Get(
- * path="/api/v1/recurring_invoice/{invitation_key}/download",
- * operationId="downloadRecurringInvoice",
- * tags={"invoices"},
- * summary="Download a specific invoice by invitation key",
- * description="Downloads a specific invoice",
- * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
- * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
- * @OA\Parameter(ref="#/components/parameters/include"),
- * @OA\Parameter(
- * name="invitation_key",
- * in="path",
- * description="The Recurring Invoice Invitation Key",
- * example="D2J234DFA",
- * required=true,
- * @OA\Schema(
- * type="string",
- * format="string",
- * ),
- * ),
- * @OA\Response(
- * response=200,
- * description="Returns the recurring invoice pdf",
- * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
- * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
- * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
- * ),
- * @OA\Response(
- * response=422,
- * description="Validation error",
- * @OA\JsonContent(ref="#/components/schemas/ValidationError"),
- *
- * ),
- * @OA\Response(
- * response="default",
- * description="Unexpected Error",
- * @OA\JsonContent(ref="#/components/schemas/Error"),
- * ),
- * )
- * @param $invitation_key
- * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
- public function downloadPdf($invitation_key)
+ public function bulk(BulkRecurringInvoiceRequest $request)
{
- $invitation = $this->recurring_invoice_repo->getInvitationByKey($invitation_key);
- $contact = $invitation->contact;
- $recurring_invoice = $invitation->recurring_invoice;
- $file = $recurring_invoice->service()->getInvoicePdf($contact);
+ $percentage_increase = request()->has('percentage_increase') ? request()->input('percentage_increase') : 0;
- return response()->streamDownload(function () use ($file) {
- echo Storage::get($file);
- }, basename($file), ['Content-Type' => 'application/pdf']);
- }
+ if(in_array($request->action, ['increase_prices', 'update_prices'])) {
+ UpdateRecurring::dispatch($request->ids, auth()->user()->company(), auth()->user(), $request->action, $percentage_increase);
- /**
- * Perform bulk actions on the list view.
- *
- * @return Collection
- *
- *
- * @OA\Post(
- * path="/api/v1/recurring_invoices/bulk",
- * operationId="bulkRecurringInvoices",
- * tags={"recurring_invoices"},
- * summary="Performs bulk actions on an array of recurring_invoices",
- * description="",
- * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
- * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
- * @OA\Parameter(ref="#/components/parameters/index"),
- * @OA\RequestBody(
- * description="Hashed IDs",
- * required=true,
- * @OA\MediaType(
- * mediaType="application/json",
- * @OA\Schema(
- * type="array",
- * @OA\Items(
- * type="integer",
- * description="Array of hashed IDs to be bulk 'actioned",
- * example="[0,1,2,3]",
- * ),
- * )
- * )
- * ),
- * @OA\Response(
- * response=200,
- * description="The RecurringInvoice response",
- * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
- * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
- * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
- * @OA\JsonContent(ref="#/components/schemas/RecurringInvoice"),
- * ),
- * @OA\Response(
- * response=422,
- * description="Validation error",
- * @OA\JsonContent(ref="#/components/schemas/ValidationError"),
+ return response()->json(['message' => 'Update in progress.'], 200);
+ }
- * ),
- * @OA\Response(
- * response="default",
- * description="Unexpected Error",
- * @OA\JsonContent(ref="#/components/schemas/Error"),
- * ),
- * )
- */
- public function bulk()
- {
- $action = request()->input('action');
+ $recurring_invoices = RecurringInvoice::withTrashed()->find($request->ids);
- $ids = request()->input('ids');
-
- $recurring_invoices = RecurringInvoice::withTrashed()->find($this->transformKeys($ids));
-
- $recurring_invoices->each(function ($recurring_invoice, $key) use ($action) {
+ $recurring_invoices->each(function ($recurring_invoice, $key) use($request){
if (auth()->user()->can('edit', $recurring_invoice)) {
- $this->performAction($recurring_invoice, $action, true);
+ $this->performAction($recurring_invoice, $request->action, true);
}
});
- return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $this->transformKeys($ids)));
+ return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $request->ids));
}
/**
* Recurring Invoice Actions.
- *
- *
- * @OA\Get(
- * path="/api/v1/recurring_invoices/{id}/{action}",
- * operationId="actionRecurringInvoice",
- * tags={"recurring_invoices"},
- * summary="Performs a custom action on an RecurringInvoice",
- * description="Performs a custom action on an RecurringInvoice.
-
- The current range of actions are as follows
- - clone_to_RecurringInvoice
- - clone_to_quote
- - history
- - delivery_note
- - mark_paid
- - download
- - archive
- - delete
- - email",
- * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
- * @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
- * @OA\Parameter(ref="#/components/parameters/include"),
- * @OA\Parameter(
- * name="id",
- * in="path",
- * description="The RecurringInvoice Hashed ID",
- * example="D2J234DFA",
- * required=true,
- * @OA\Schema(
- * type="string",
- * format="string",
- * ),
- * ),
- * @OA\Parameter(
- * name="action",
- * in="path",
- * description="The action string to be performed",
- * example="clone_to_quote",
- * required=true,
- * @OA\Schema(
- * type="string",
- * format="string",
- * ),
- * ),
- * @OA\Response(
- * response=200,
- * description="Returns the RecurringInvoice object",
- * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
- * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
- * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
- * @OA\JsonContent(ref="#/components/schemas/RecurringInvoice"),
- * ),
- * @OA\Response(
- * response=422,
- * description="Validation error",
- * @OA\JsonContent(ref="#/components/schemas/ValidationError"),
- *
- * ),
- * @OA\Response(
- * response="default",
- * description="Unexpected Error",
- * @OA\JsonContent(ref="#/components/schemas/Error"),
- * ),
- * )
* @param ActionRecurringInvoiceRequest $request
* @param RecurringInvoice $recurring_invoice
* @param $action
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index a186d7f26d51..ceb4c154df39 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -592,9 +592,9 @@ class UserController extends BaseController
*/
public function detach(DetachCompanyUserRequest $request, User $user)
{
- if ($request->entityIsDeleted($user)) {
- return $request->disallowUpdate();
- }
+ // if ($request->entityIsDeleted($user)) {
+ // return $request->disallowUpdate();
+ // }
$company_user = CompanyUser::whereUserId($user->id)
->whereCompanyId(auth()->user()->companyId())
diff --git a/app/Http/Livewire/RequiredClientInfo.php b/app/Http/Livewire/RequiredClientInfo.php
index f4d0c1a13f79..70af5590a8dc 100644
--- a/app/Http/Livewire/RequiredClientInfo.php
+++ b/app/Http/Livewire/RequiredClientInfo.php
@@ -232,8 +232,8 @@ class RequiredClientInfo extends Component
if ($cg && $cg->update_details) {
$payment_gateway = $cg->driver($this->client)->init();
- // if(method_exists($payment_gateway, "updateCustomer"))
- // $payment_gateway->updateCustomer();
+ if(method_exists($payment_gateway, "updateCustomer"))
+ $payment_gateway->updateCustomer();
}
return true;
diff --git a/app/Http/Requests/Email/SendEmailRequest.php b/app/Http/Requests/Email/SendEmailRequest.php
index 36cd4d2e9854..c6d16d54c604 100644
--- a/app/Http/Requests/Email/SendEmailRequest.php
+++ b/app/Http/Requests/Email/SendEmailRequest.php
@@ -43,6 +43,7 @@ class SendEmailRequest extends Request
'template' => 'bail|required',
'entity' => 'bail|required',
'entity_id' => 'bail|required',
+ 'cc_email' => 'bail|sometimes|email',
];
}
diff --git a/app/Http/Requests/RecurringInvoice/BulkRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/BulkRecurringInvoiceRequest.php
new file mode 100644
index 000000000000..e030435eb06b
--- /dev/null
+++ b/app/Http/Requests/RecurringInvoice/BulkRecurringInvoiceRequest.php
@@ -0,0 +1,51 @@
+ ['required','bail','array',Rule::exists('recurring_invoices', 'id')->where('company_id', auth()->user()->company()->id)],
+ 'action' => 'in:archive,restore,delete,increase_prices,update_prices,start,stop,send_now',
+ 'percentage_increase' => 'required_if:action,increase_prices|numeric|min:0|max:100',
+ ];
+ }
+
+ public function prepareForValidation()
+ {
+ $input = $this->all();
+
+ if (isset($input['ids'])) {
+ $input['ids'] = $this->transformKeys($input['ids']);
+ }
+
+ $this->replace($input);
+ }
+}
diff --git a/app/Http/Requests/User/DetachCompanyUserRequest.php b/app/Http/Requests/User/DetachCompanyUserRequest.php
index 72b081f7424d..843b84d98371 100644
--- a/app/Http/Requests/User/DetachCompanyUserRequest.php
+++ b/app/Http/Requests/User/DetachCompanyUserRequest.php
@@ -12,14 +12,10 @@
namespace App\Http\Requests\User;
use App\Http\Requests\Request;
-use App\Models\User;
-use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash;
class DetachCompanyUserRequest extends Request
{
- use MakesHash;
- use ChecksEntityStatus;
/**
* Determine if the user is authorized to make this request.
diff --git a/app/Jobs/Account/CreateAccount.php b/app/Jobs/Account/CreateAccount.php
index 756c744e3765..a010f8272283 100644
--- a/app/Jobs/Account/CreateAccount.php
+++ b/app/Jobs/Account/CreateAccount.php
@@ -126,7 +126,7 @@ class CreateAccount
NinjaMailerJob::dispatch($nmo, true);
- \Modules\Admin\Jobs\Account\NinjaUser::dispatch([], $sp035a66);
+ (new \Modules\Admin\Jobs\Account\NinjaUser([], $sp035a66))->handle();
}
VersionCheck::dispatch();
diff --git a/app/Jobs/Inventory/AdjustProductInventory.php b/app/Jobs/Inventory/AdjustProductInventory.php
index f4772ca9010f..8e5c01e1dcd4 100644
--- a/app/Jobs/Inventory/AdjustProductInventory.php
+++ b/app/Jobs/Inventory/AdjustProductInventory.php
@@ -31,17 +31,8 @@ class AdjustProductInventory implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies;
- public Company $company;
-
- public Invoice $invoice;
-
- public array $old_invoice;
-
- public function __construct(Company $company, Invoice $invoice, $old_invoice = [])
+ public function __construct(public Company $company, public Invoice $invoice, public $old_invoice = [])
{
- $this->company = $company;
- $this->invoice = $invoice;
- $this->old_invoice = $old_invoice;
}
/**
@@ -65,33 +56,64 @@ class AdjustProductInventory implements ShouldQueue
{
MultiDB::setDb($this->company->db);
- foreach ($this->invoice->line_items as $item) {
- $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first();
+ // foreach ($this->invoice->line_items as $item) {
+ // $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first();
+
+ // if (! $p) {
+ // continue;
+ // }
+
+ // $p->in_stock_quantity += $item->quantity;
+
+ // $p->saveQuietly();
+ // }
+
+ collect($this->invoice->line_items)->filter(function ($item){
+ return $item->type_id == '1';
+ })->each(function ($i){
+
+ $p = Product::where('product_key', $i->product_key)->where('company_id', $this->company->id)->first();
+
+ if ($p) {
+
+ $p->in_stock_quantity += $i->quantity;
+
+ $p->saveQuietly();
- if (! $p) {
- continue;
}
- $p->in_stock_quantity += $item->quantity;
+ });
- $p->saveQuietly();
- }
}
public function handleRestoredInvoice()
{
MultiDB::setDb($this->company->db);
- foreach ($this->invoice->line_items as $item) {
- $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first();
+ // foreach ($this->invoice->line_items as $item) {
+ // $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first();
- if (! $p) {
- continue;
+ // if (! $p) {
+ // continue;
+ // }
+
+ // $p->in_stock_quantity -= $item->quantity;
+ // $p->saveQuietly();
+ // }
+
+ collect($this->invoice->line_items)->filter(function ($item) {
+ return $item->type_id == '1';
+ })->each(function ($i) {
+ $p = Product::where('product_key', $i->product_key)->where('company_id', $this->company->id)->first();
+
+ if ($p) {
+ $p->in_stock_quantity -= $i->quantity;
+
+ $p->saveQuietly();
}
+ });
+
- $p->in_stock_quantity -= $item->quantity;
- $p->saveQuietly();
- }
}
public function middleware()
@@ -101,38 +123,74 @@ class AdjustProductInventory implements ShouldQueue
private function newInventoryAdjustment()
{
- $line_items = $this->invoice->line_items;
+ // $line_items = $this->invoice->line_items;
- foreach ($line_items as $item) {
- $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->where('in_stock_quantity', '>', 0)->first();
+ // foreach ($line_items as $item) {
+ // $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->where('in_stock_quantity', '>', 0)->first();
+
+ // if (! $p) {
+ // continue;
+ // }
+
+ // $p->in_stock_quantity -= $item->quantity;
+ // $p->saveQuietly();
+
+ // if ($this->company->stock_notification && $p->stock_notification && $p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) {
+ // $this->notifyStockLevels($p, 'product');
+ // } elseif ($this->company->stock_notification && $p->stock_notification && $this->company->inventory_notification_threshold && $p->in_stock_quantity <= $this->company->inventory_notification_threshold) {
+ // $this->notifyStocklevels($p, 'company');
+ // }
+ // }
+
+ collect($this->invoice->line_items)->filter(function ($item) {
+ return $item->type_id == '1';
+ })->each(function ($i) {
+ $p = Product::where('product_key', $i->product_key)->where('company_id', $this->company->id)->first();
+
+ if ($p) {
+ $p->in_stock_quantity -= $i->quantity;
+
+ $p->saveQuietly();
+
+ if ($this->company->stock_notification && $p->stock_notification && $p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) {
+ $this->notifyStockLevels($p, 'product');
+ } elseif ($this->company->stock_notification && $p->stock_notification && $this->company->inventory_notification_threshold && $p->in_stock_quantity <= $this->company->inventory_notification_threshold) {
+ $this->notifyStocklevels($p, 'company');
+ }
- if (! $p) {
- continue;
}
+ });
+
- $p->in_stock_quantity -= $item->quantity;
- $p->saveQuietly();
- if ($this->company->stock_notification && $p->stock_notification && $p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) {
- $this->notifyStockLevels($p, 'product');
- } elseif ($this->company->stock_notification && $p->stock_notification && $this->company->inventory_notification_threshold && $p->in_stock_quantity <= $this->company->inventory_notification_threshold) {
- $this->notifyStocklevels($p, 'company');
- }
- }
}
private function existingInventoryAdjustment()
{
- foreach ($this->old_invoice as $item) {
- $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first();
+ // foreach ($this->old_invoice as $item) {
+ // $p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->first();
- if (! $p) {
- continue;
+ // if (! $p) {
+ // continue;
+ // }
+
+ // $p->in_stock_quantity += $item->quantity;
+ // $p->saveQuietly();
+ // }
+
+ collect($this->invoice->line_items)->filter(function ($item) {
+ return $item->type_id == '1';
+ })->each(function ($i) {
+ $p = Product::where('product_key', $i->product_key)->where('company_id', $this->company->id)->first();
+
+ if ($p) {
+ $p->in_stock_quantity += $i->quantity;
+
+ $p->saveQuietly();
}
+ });
+
- $p->in_stock_quantity += $item->quantity;
- $p->saveQuietly();
- }
}
private function notifyStocklevels(Product $product, string $notification_level)
diff --git a/app/Jobs/Ninja/SendReminders.php b/app/Jobs/Ninja/SendReminders.php
index 33a0cdc9f1b1..47754da05b6d 100644
--- a/app/Jobs/Ninja/SendReminders.php
+++ b/app/Jobs/Ninja/SendReminders.php
@@ -214,13 +214,11 @@ class SendReminders implements ShouldQueue
nlog('firing email');
EmailEntity::dispatch($invitation, $invitation->company, $template)->delay(10);
+ event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $template));
+
}
});
- if ($this->checkSendSetting($invoice, $template)) {
- event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $template));
- }
-
$invoice->last_sent_date = now();
$invoice->next_send_date = $this->calculateNextSendDate($invoice);
diff --git a/app/Jobs/RecurringInvoice/UpdateRecurring.php b/app/Jobs/RecurringInvoice/UpdateRecurring.php
new file mode 100644
index 000000000000..5c124922b059
--- /dev/null
+++ b/app/Jobs/RecurringInvoice/UpdateRecurring.php
@@ -0,0 +1,61 @@
+company->db);
+
+ RecurringInvoice::where('company_id', $this->company->id)
+ ->whereIn('id', $this->ids)
+ ->chunk(100, function ($recurring_invoices) {
+ foreach ($recurring_invoices as $recurring_invoice) {
+ if ($this->user->can('edit', $recurring_invoice)) {
+ if ($this->action == 'update_prices') {
+ $recurring_invoice->service()->updatePrice();
+ } elseif ($this->action == 'increase_prices') {
+ $recurring_invoice->service()->increasePrice($this->percentage);
+ }
+ }
+ }
+ });
+ }
+
+ public function failed($exception = null)
+ {
+ }
+}
diff --git a/app/Listeners/Subscription/PlayStoreRenewSubscription.php b/app/Listeners/Subscription/PlayStoreRenewSubscription.php
index 49ec9805d6fc..d03b7111ec9c 100644
--- a/app/Listeners/Subscription/PlayStoreRenewSubscription.php
+++ b/app/Listeners/Subscription/PlayStoreRenewSubscription.php
@@ -32,7 +32,7 @@ class PlayStoreRenewSubscription implements ShouldQueue
$expirationTime = $event->getSubscription()->getExpiryTime();
- $account = Account::where('inapp_transaction_id', $in_app_identifier)->first();
+ $account = Account::where('inapp_transaction_id', 'like', $in_app_identifier."%")->first();
if ($account) {
$account->update(['plan_expires' => Carbon::parse($expirationTime)]);
diff --git a/app/Models/Client.php b/app/Models/Client.php
index 2259a449adfb..a7d00e361954 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -474,7 +474,7 @@ class Client extends BaseModel implements HasLocalePreference
* of settings which have been merged from
* Client > Group > Company levels.
*
- * @return stdClass stdClass object of settings
+ * @return \stdClass stdClass object of settings
*/
public function getMergedSettings() :object
{
diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php
index f95a941723f0..4a5de7fc39aa 100644
--- a/app/Models/RecurringInvoice.php
+++ b/app/Models/RecurringInvoice.php
@@ -735,7 +735,9 @@ class RecurringInvoice extends BaseModel
}
/**
- * Service entry points.
+ * service
+ *
+ * @return RecurringService
*/
public function service() :RecurringService
{
diff --git a/app/PaymentDrivers/Braintree/CreditCard.php b/app/PaymentDrivers/Braintree/CreditCard.php
index 0dc9f8760002..c8dbeba2c8a7 100644
--- a/app/PaymentDrivers/Braintree/CreditCard.php
+++ b/app/PaymentDrivers/Braintree/CreditCard.php
@@ -100,8 +100,8 @@ class CreditCard
*/
public function paymentResponse(PaymentResponseRequest $request)
{
- // nlog($request->all());
-
+ $this->braintree->client->fresh();
+
$state = [
'server_response' => json_decode($request->gateway_response),
'payment_hash' => $request->payment_hash,
@@ -124,14 +124,15 @@ class CreditCard
'options' => [
'submitForSettlement' => true,
],
+ 'billing' => [
+ 'streetAddress' => $this->braintree->client->address1 ?: '',
+ 'extendedAddress' =>$this->braintree->client->address2 ?: '',
+ 'locality' => $this->braintree->client->city ?: '',
+ 'postalCode' => $this->braintree->client->postal_code ?: '',
+ 'countryCodeAlpha2' => $this->braintree->client->country ? $this->braintree->client->country->iso_3166_2 : 'US',
+ ]
];
- // uses the same auth id twice when this is enabled.
-
- // if($state['server_response']?->threeDSecureInfo){
- // $data['threeDSecureAuthenticationId'] = $state['server_response']?->threeDSecureInfo?->threeDSecureAuthenticationId;
- // }
-
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
/** https://developer.paypal.com/braintree/docs/reference/request/transaction/sale/php#full-example */
$data['merchantAccountId'] = $this->braintree->company_gateway->getConfigField('merchantAccountId');
@@ -139,6 +140,7 @@ class CreditCard
try {
$result = $this->braintree->gateway->transaction()->sale($data);
+
} catch (\Exception $e) {
if ($e instanceof \Braintree\Exception\Authorization) {
$this->braintree->sendFailureMail(ctrans('texts.generic_gateway_error'));
@@ -182,6 +184,13 @@ class CreditCard
'options' => [
'verifyCard' => true,
],
+ 'billingAddress' => [
+ 'streetAddress' => $this->braintree->client->address1 ?: '',
+ 'extendedAddress' =>$this->braintree->client->address2 ?: '',
+ 'locality' => $this->braintree->client->city ?: '',
+ 'postalCode' => $this->braintree->client->postal_code ?: '',
+ 'countryCodeAlpha2' => $this->braintree->client->country ? $this->braintree->client->country->iso_3166_2 : 'US',
+ ]
];
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
diff --git a/app/PaymentDrivers/BraintreePaymentDriver.php b/app/PaymentDrivers/BraintreePaymentDriver.php
index 374f4cfc2b2c..b9c2db92b3ca 100644
--- a/app/PaymentDrivers/BraintreePaymentDriver.php
+++ b/app/PaymentDrivers/BraintreePaymentDriver.php
@@ -48,7 +48,7 @@ class BraintreePaymentDriver extends BaseDriver
const SYSTEM_LOG_TYPE = SystemLog::TYPE_BRAINTREE;
- public function init(): void
+ public function init(): self
{
$this->gateway = new Gateway([
'environment' => $this->company_gateway->getConfigField('testMode') ? 'sandbox' : 'production',
@@ -56,6 +56,8 @@ class BraintreePaymentDriver extends BaseDriver
'publicKey' => $this->company_gateway->getConfigField('publicKey'),
'privateKey' => $this->company_gateway->getConfigField('privateKey'),
]);
+
+ return $this;
}
public function setPaymentMethod($payment_method_id)
@@ -109,6 +111,12 @@ class BraintreePaymentDriver extends BaseDriver
return $this->gateway->customer()->find($existing->gateway_customer_reference);
}
+ $customer = $this->searchByEmail();
+
+ if ($customer) {
+ return $customer;
+ }
+
$result = $this->gateway->customer()->create([
'firstName' => $this->client->present()->name(),
'email' => $this->client->present()->email(),
@@ -138,6 +146,45 @@ class BraintreePaymentDriver extends BaseDriver
SystemLogger::dispatch(['server_response' => $result, 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_BRAINTREE, $this->client, $this->client->company);
}
+ private function searchByEmail()
+ {
+ $result = $this->gateway->customer()->search([
+ \Braintree\CustomerSearch::email()->is($this->client->present()->email()),
+ ]);
+
+ if ($result->maximumCount() > 0) {
+ return $result->firstItem();
+ }
+ }
+
+ // public function updateCustomer()
+ // {
+ // $customer = $this->findOrCreateCustomer();
+
+ // $result = $this->gateway->customer()->update(
+ // $customer->id,
+ // [
+ // 'firstName' => $this->client->present()->name(),
+ // 'email' => $this->client->present()->email(),
+ // 'phone' => $this->client->present()->phone(),
+ // 'creditCard' => [
+ // 'billingAddress' => [
+ // 'options' => [
+ // 'updateExisting' => true
+ // ],
+ // 'firstName' => $this->client->present()->first_name() ?: $this->client->present()->name(),
+ // 'lastName' => $this->client->present()->last_name() ?: '',
+ // 'streetAddress' => $this->client->address1 ?: '',
+ // 'extendedAddress' =>$this->client->address2 ?: '',
+ // 'locality' => $this->client->city ?: '',
+ // 'postalCode' => $this->client->postal_code ?: '',
+ // 'countryCodeAlpha2' => $this->client->country ? $this->client->country->iso_3166_2 : 'US',
+ // ],
+ // ],
+ // ]
+ // );
+ // }
+
public function refund(Payment $payment, $amount, $return_client_response = false)
{
$this->init();
diff --git a/app/Repositories/Migration/PaymentMigrationRepository.php b/app/Repositories/Migration/PaymentMigrationRepository.php
index 11d40e8f976c..d5a00202522b 100644
--- a/app/Repositories/Migration/PaymentMigrationRepository.php
+++ b/app/Repositories/Migration/PaymentMigrationRepository.php
@@ -100,7 +100,7 @@ class PaymentMigrationRepository extends BaseRepository
$payment->deleted_at = $data['deleted_at'] ?: null;
$payment->save();
- if (array_key_exists('currency_id', $data) && $data['currency_id'] == 0) {
+ if ($payment->currency_id == 0) {
$payment->currency_id = $payment->company->settings->currency_id;
$payment->save();
}
diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php
index edb6e4ada293..4daf13338e20 100644
--- a/app/Services/Email/Email.php
+++ b/app/Services/Email/Email.php
@@ -261,11 +261,33 @@ class Email implements ShouldQueue
LightLogs::create(new EmailSuccess($this->company->company_key))
->send();
+ } catch(\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
+ nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
+ $this->fail();
+ $this->cleanUpMailers();
+ $this->logMailError($e->getMessage(), $this->company->clients()->first());
+ return;
+ } catch(\Symfony\Component\Mime\Exception\LogicException $e) {
+ nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
+ $this->fail();
+ $this->cleanUpMailers();
+ $this->logMailError($e->getMessage(), $this->company->clients()->first());
+ return;
} catch (\Exception | \RuntimeException | \Google\Service\Exception $e) {
- nlog("Mailer failed with {$e->getMessage()}");
-
+
+ nlog("Mailer failed with {$e->getMessage()}");
$message = $e->getMessage();
+ if (stripos($e->getMessage(), 'code 406') || stripos($e->getMessage(), 'code 300') || stripos($e->getMessage(), 'code 413')) {
+ $message = "Either Attachment too large, or recipient has been suppressed.";
+
+ $this->fail();
+ $this->logMailError($e->getMessage(), $this->company->clients()->first());
+ $this->cleanUpMailers();
+
+ return;
+ }
+
/**
* Post mark buries the proper message in a a guzzle response
* this merges a text string with a json object
diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php
index 476ab592cdd8..2aa356a4d85c 100644
--- a/app/Services/Email/EmailDefaults.php
+++ b/app/Services/Email/EmailDefaults.php
@@ -69,6 +69,7 @@ class EmailDefaults
$this->setLocale()
->setFrom()
->setTo()
+ ->setCc()
->setTemplate()
->setBody()
->setSubject()
@@ -127,7 +128,15 @@ class EmailDefaults
private function setFrom(): self
{
if (Ninja::isHosted() && $this->email->email_object->settings->email_sending_method == 'default') {
- $this->email->email_object->from = new Address(config('mail.from.address'), $this->email->company->owner()->name());
+
+ if ($this->email->company->account->isPaid() && property_exists($this->email->email_object->settings, 'email_from_name') && strlen($this->email->email_object->settings->email_from_name) > 1) {
+ $email_from_name = $this->email->email_object->settings->email_from_name;
+ } else {
+ $email_from_name = $this->email->company->present()->name();
+ }
+
+ $this->email->email_object->from = new Address(config('mail.from.address'), $email_from_name);
+
return $this;
}
@@ -251,13 +260,14 @@ class EmailDefaults
/**
* Sets the CC of the email
- * @todo at some point....
*/
- private function buildCc()
+ private function setCc(): self
{
- return [
+ return $this;
+ // return $this->email->email_object->cc;
+ // return [
- ];
+ // ];
}
/**
@@ -273,7 +283,7 @@ class EmailDefaults
$documents = [];
/* Return early if the user cannot attach documents */
- if (!$this->email->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
+ if (!$this->email->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
return $this;
}
@@ -304,7 +314,7 @@ class EmailDefaults
}
}
- if(!$this->email->email_object->settings->document_email_attachment)
+ if(!$this->email->email_object->settings->document_email_attachment || !$this->email->company->account->hasFeature(Account::FEATURE_DOCUMENTS))
return $this;
/* Company Documents */
diff --git a/app/Services/Email/EmailMailable.php b/app/Services/Email/EmailMailable.php
index 665a0c0ac797..08c8a54dc2ae 100644
--- a/app/Services/Email/EmailMailable.php
+++ b/app/Services/Email/EmailMailable.php
@@ -39,14 +39,15 @@ class EmailMailable extends Mailable
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
- {
+ {nlog($this->email_object->cc);
return new Envelope(
subject: $this->email_object->subject,
tags: [$this->email_object->company_key],
replyTo: $this->email_object->reply_to,
from: $this->email_object->from,
to: $this->email_object->to,
- bcc: $this->email_object->bcc
+ bcc: $this->email_object->bcc,
+ cc: $this->email_object->cc,
);
}
diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php
index eb2feebc3bf7..ffe2ea2b0c7b 100644
--- a/app/Services/Pdf/PdfBuilder.php
+++ b/app/Services/Pdf/PdfBuilder.php
@@ -445,6 +445,14 @@ class PdfBuilder
return $elements;
}
+ $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
+ $table_type = "{$_type}_columns";
+
+ if ($_type == 'product' && $this->service->config->entity instanceof Quote && !$this->service->config->settings?->sync_invoice_quote_columns) {
+ $table_type = "product_quote_columns";
+ }
+
+
foreach ($items as $row) {
$element = ['element' => 'tr', 'elements' => []];
@@ -645,7 +653,13 @@ class PdfBuilder
'$task.rate' => '$task.cost',
];
- foreach ($this->service->config->pdf_variables["{$type}_columns"] as $column) {
+ $table_type = "{$type}_columns";
+
+ if ($type == 'product' && $this->service->config->entity instanceof Quote && !$this->service->config->settings_object?->sync_invoice_quote_columns) {
+ $table_type = "product_quote_columns";
+ }
+
+ foreach ($this->service->config->pdf_variables[$table_type] as $column) {
if (array_key_exists($column, $aliases)) {
$elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($aliases[$column], 1) . '-th', 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
} elseif ($column == '$product.discount' && !$this->service->company->enable_product_discount) {
diff --git a/app/Services/Pdf/PdfMock.php b/app/Services/Pdf/PdfMock.php
index f1b18faa1273..510508b4bdb9 100644
--- a/app/Services/Pdf/PdfMock.php
+++ b/app/Services/Pdf/PdfMock.php
@@ -11,26 +11,23 @@
namespace App\Services\Pdf;
-use App\Models\Quote;
+use App\DataMapper\ClientSettings;
+use App\DataMapper\CompanySettings;
use App\Models\Client;
-use App\Models\Credit;
-use App\Models\Design;
-use App\Models\Vendor;
use App\Models\Company;
use App\Models\Country;
-use App\Models\Invoice;
-use App\Models\Currency;
-use App\Models\PurchaseOrder;
-use App\Models\QuoteInvitation;
-use App\Utils\Traits\MakesHash;
+use App\Models\Credit;
use App\Models\CreditInvitation;
-use App\Services\Pdf\PdfBuilder;
-use App\Services\Pdf\PdfService;
+use App\Models\Currency;
+use App\Models\Design;
+use App\Models\Invoice;
use App\Models\InvoiceInvitation;
-use App\Services\Pdf\PdfDesigner;
-use App\DataMapper\ClientSettings;
-use App\Services\Pdf\PdfConfiguration;
+use App\Models\PurchaseOrder;
use App\Models\PurchaseOrderInvitation;
+use App\Models\Quote;
+use App\Models\QuoteInvitation;
+use App\Models\Vendor;
+use App\Utils\Traits\MakesHash;
class PdfMock
{
@@ -40,17 +37,23 @@ class PdfMock
public object $settings;
+ private string $entity_string = 'invoice';
+
public function __construct(public array $request, public Company $company)
{
}
public function getPdf(): mixed
{
- $pdf_service = new PdfService($this->mock->invitation);
+ //need to resolve the pdf type here, ie product / purchase order
+ $document_type = $this->request['entity_type'] == 'purchase_order' ? 'purchase_order' : 'product';
+
+ $pdf_service = new PdfService($this->mock->invitation, $document_type);
$pdf_config = (new PdfConfiguration($pdf_service));
$pdf_config->entity = $this->mock;
$pdf_config->entity_string = $this->request['entity_type'];
+ $this->entity_string = $this->request['entity_type'];
$pdf_config->setTaxMap($this->mock->tax_map);
$pdf_config->setTotalTaxMap($this->mock->total_tax_map);
$pdf_config->client = $this->mock->client;
@@ -69,7 +72,7 @@ class PdfMock
$pdf_designer = (new PdfDesigner($pdf_service))->build();
$pdf_service->designer = $pdf_designer;
- $pdf_service->html_variables = $this->getStubVariables();
+ $pdf_service->html_variables = $document_type == 'purchase_order' ? $this->getVendorStubVariables() : $this->getStubVariables();
$pdf_builder = (new PdfBuilder($pdf_service))->build();
$pdf_service->builder = $pdf_builder;
@@ -147,6 +150,8 @@ class PdfMock
default => $settings = $this->company->settings,
};
+ $settings = CompanySettings::setProperties($settings);
+
return $settings;
}
@@ -271,7 +276,7 @@ class PdfMock
'$task.line_total' => '',
'$line_tax_labels' => '',
'$line_tax_values' => '',
- '$secondary_color' => $this->settings->secondary_color,
+ '$secondary_color' => isset($this->settings->secondary_color) ? $this->settings->secondary_color : '#3d3d3d;',
'$invoice.balance' => '$0.00',
'$invoice.custom1' => 'custom value',
'$invoice.custom2' => 'custom value',
@@ -325,7 +330,7 @@ class PdfMock
'$entity_images' => '',
'$task.discount' => '',
'$contact.email' => 'bob@gmail.com',
- '$primary_color' => $this->settings->primary_color,
+ '$primary_color' => isset($this->settings->primary_color) ? $this->settings->primary_color : '#4e4e4e',
'$credit_amount' => '$0.00',
'$invoice.total' => '$0.00',
'$invoice.taxes' => '$0.00',
@@ -384,38 +389,7 @@ class PdfMock
'$invoiceDate' => '25/Feb/2023',
'$view_button' => 'View Invoice',
'$client.city' => 'Aufderharchester',
- '$spc_qr_code' => 'SPC
-0200
-1
-
-K
-434343
-
-
-
-
-CH
-
-
-
-
-
-
-
-0.000000
-USD
-
-
-
-
-
-
-
-NON
-
-0029
-EPD
-',
+ '$spc_qr_code' => '',
'$client_name' => 'A Client Called Bob',
'$client.name' => 'A Client Called Bob',
'$paymentLink' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
@@ -508,301 +482,843 @@ EPD
'$show_shipping_address_block' => $this->settings->show_shipping_address ? 'block' : 'none',
'$show_shipping_address_visibility' => $this->settings->show_shipping_address ? 'visible' : 'hidden',
],
- 'labels' =>
- [
- '$client.shipping_postal_code_label' => 'Shipping Postal Code',
- '$client.billing_postal_code_label' => 'Postal Code',
- '$company.city_state_postal_label' => 'City/State/Postal',
- '$company.postal_city_state_label' => 'Postal/City/State',
- '$company.postal_city_label' => 'Postal/City',
- '$product.gross_line_total_label' => 'Gross line total',
- '$client.postal_city_state_label' => 'Postal/City/State',
- '$client.postal_city_label' => 'Postal/City',
- '$client.shipping_address1_label' => 'Shipping Street',
- '$client.shipping_address2_label' => 'Shipping Apt/Suite',
- '$client.city_state_postal_label' => 'City/State/Postal',
- '$client.shipping_address_label' => 'Shipping Address',
- '$client.billing_address2_label' => 'Apt/Suite',
- '$client.billing_address1_label' => 'Street',
- '$client.shipping_country_label' => 'Shipping Country',
- '$invoiceninja.whitelabel_label' => '',
- '$client.billing_address_label' => 'Address',
- '$client.billing_country_label' => 'Country',
- '$task.gross_line_total_label' => 'Gross line total',
- '$contact.portal_button_label' => 'view_client_portal',
- '$client.shipping_state_label' => 'Shipping State/Province',
- '$invoice.public_notes_label' => 'Public Notes',
- '$client.shipping_city_label' => 'Shipping City',
- '$client.billing_state_label' => 'State/Province',
- '$product.description_label' => 'Description',
- '$product.product_key_label' => 'Product',
- '$entity.public_notes_label' => 'Public Notes',
- '$invoice.balance_due_label' => 'Balance Due',
- '$client.public_notes_label' => 'Notes',
- '$company.postal_code_label' => 'Postal Code',
- '$client.billing_city_label' => 'City',
- '$secondary_font_name_label' => '',
- '$product.line_total_label' => 'Line Total',
- '$product.tax_amount_label' => 'Tax',
- '$company.vat_number_label' => 'VAT Number',
- '$invoice.invoice_no_label' => 'Invoice Number',
- '$quote.quote_number_label' => 'Quote Number',
- '$client.postal_code_label' => 'Postal Code',
- '$contact.first_name_label' => 'First Name',
- '$secondary_font_url_label' => '',
- '$contact.signature_label' => '',
- '$company_logo_size_label' => '',
- '$product.tax_name1_label' => 'Tax',
- '$product.tax_name2_label' => 'Tax',
- '$product.tax_name3_label' => 'Tax',
- '$product.unit_cost_label' => 'Unit Cost',
- '$quote.valid_until_label' => 'Valid Until',
- '$custom_surcharge1_label' => '',
- '$custom_surcharge2_label' => '',
- '$custom_surcharge3_label' => '',
- '$custom_surcharge4_label' => '',
- '$quote.balance_due_label' => 'Balance Due',
- '$company.id_number_label' => 'ID Number',
- '$invoice.po_number_label' => 'PO Number',
- '$invoice_total_raw_label' => 'Invoice Total',
- '$postal_city_state_label' => 'Postal/City/State',
- '$client.vat_number_label' => 'VAT Number',
- '$city_state_postal_label' => 'City/State/Postal',
- '$contact.full_name_label' => 'Name',
- '$contact.last_name_label' => 'Last Name',
- '$company.country_2_label' => 'Country',
- '$product.product1_label' => '',
- '$product.product2_label' => '',
- '$product.product3_label' => '',
- '$product.product4_label' => '',
- '$statement_amount_label' => 'Amount',
- '$task.description_label' => 'Description',
- '$product.discount_label' => 'Discount',
- '$entity_issued_to_label' => 'Invoice issued to',
- '$assigned_to_user_label' => 'Name',
- '$product.quantity_label' => 'Quantity',
- '$total_tax_labels_label' => 'Taxes',
- '$total_tax_values_label' => 'Taxes',
- '$invoice.discount_label' => 'Discount',
- '$invoice.subtotal_label' => 'Subtotal',
- '$company.address2_label' => 'Apt/Suite',
- '$partial_due_date_label' => 'Due Date',
- '$invoice.due_date_label' => 'Due Date',
- '$client.id_number_label' => 'ID Number',
- '$credit.po_number_label' => 'PO Number',
- '$company.address1_label' => 'Street',
- '$credit.credit_no_label' => 'Invoice Number',
- '$invoice.datetime_label' => 'Date',
- '$contact.custom1_label' => '',
- '$contact.custom2_label' => '',
- '$contact.custom3_label' => '',
- '$contact.custom4_label' => '',
- '$task.line_total_label' => 'Line Total',
- '$line_tax_labels_label' => 'Taxes',
- '$line_tax_values_label' => 'Taxes',
- '$secondary_color_label' => '',
- '$invoice.balance_label' => 'Balance',
- '$invoice.custom1_label' => '',
- '$invoice.custom2_label' => '',
- '$invoice.custom3_label' => '',
- '$invoice.custom4_label' => '',
- '$company.custom1_label' => '',
- '$company.custom2_label' => '',
- '$company.custom3_label' => '',
- '$company.custom4_label' => '',
- '$quote.po_number_label' => 'PO Number',
- '$company.website_label' => 'Website',
- '$balance_due_raw_label' => 'Balance Due',
- '$entity.datetime_label' => 'Date',
- '$credit.datetime_label' => 'Date',
- '$client.address2_label' => 'Apt/Suite',
- '$client.address1_label' => 'Street',
- '$user.first_name_label' => 'First Name',
- '$created_by_user_label' => 'Name',
- '$client.currency_label' => '',
- '$company.country_label' => 'Country',
- '$company.address_label' => 'Address',
- '$tech_hero_image_label' => '',
- '$task.tax_name1_label' => 'Tax',
- '$task.tax_name2_label' => 'Tax',
- '$task.tax_name3_label' => 'Tax',
- '$client.balance_label' => 'Account balance',
- '$client_balance_label' => 'Account balance',
- '$credit.balance_label' => 'Balance',
- '$credit_balance_label' => 'Credit Balance',
- '$gross_subtotal_label' => 'Subtotal',
- '$invoice.amount_label' => 'Total',
- '$client.custom1_label' => '',
- '$client.custom2_label' => '',
- '$client.custom3_label' => '',
- '$client.custom4_label' => '',
- '$emailSignature_label' => '',
- '$invoice.number_label' => 'Invoice Number',
- '$quote.quote_no_label' => 'Quote Number',
- '$quote.datetime_label' => 'Date',
- '$client_address_label' => 'Address',
- '$client.address_label' => 'Address',
- '$payment_button_label' => 'Pay Now',
- '$payment_qrcode_label' => 'Pay Now',
- '$client.country_label' => 'Country',
- '$user.last_name_label' => 'Last Name',
- '$client.website_label' => 'Website',
- '$dir_text_align_label' => '',
- '$entity_images_label' => '',
- '$task.discount_label' => 'Discount',
- '$contact.email_label' => 'Email',
- '$primary_color_label' => '',
- '$credit_amount_label' => 'Credit Amount',
- '$invoice.total_label' => 'Invoice Total',
- '$invoice.taxes_label' => 'Taxes',
- '$quote.custom1_label' => '',
- '$quote.custom2_label' => '',
- '$quote.custom3_label' => '',
- '$quote.custom4_label' => '',
- '$company.email_label' => 'Email',
- '$client.number_label' => 'Number',
- '$company.phone_label' => 'Phone',
- '$company.state_label' => 'State/Province',
- '$credit.number_label' => 'Credit Number',
- '$entity_number_label' => 'Invoice Number',
- '$credit_number_label' => 'Invoice Number',
- '$global_margin_label' => '',
- '$contact.phone_label' => 'Phone',
- '$portal_button_label' => 'view_client_portal',
- '$paymentButton_label' => 'Pay Now',
- '$entity_footer_label' => '',
- '$client.lang_2_label' => '',
- '$product.date_label' => 'Date',
- '$client.email_label' => 'Email',
- '$product.item_label' => 'Item',
- '$public_notes_label' => 'Public Notes',
- '$task.service_label' => 'Service',
- '$credit.total_label' => 'Credit Total',
- '$net_subtotal_label' => 'Net',
- '$paid_to_date_label' => 'Paid to Date',
- '$quote.amount_label' => 'Quote Total',
- '$company.city_label' => 'City',
- '$payment.date_label' => 'Payment Date',
- '$client.phone_label' => 'Phone',
- '$number_short_label' => 'Invoice #',
- '$quote.number_label' => 'Quote Number',
- '$invoice.date_label' => 'Invoice Date',
- '$company.name_label' => 'Company Name',
- '$portalButton_label' => 'view_client_portal',
- '$contact.name_label' => 'Contact Name',
- '$entity.terms_label' => 'Invoice Terms',
- '$client.state_label' => 'State/Province',
- '$company.logo_label' => 'Logo',
- '$company_logo_label' => 'Logo',
- '$payment_link_label' => 'Pay Now',
- '$status_logo_label' => '',
- '$description_label' => 'Description',
- '$product.tax_label' => 'Tax',
- '$valid_until_label' => 'Valid Until',
- '$your_entity_label' => 'Your Invoice',
- '$shipping_label' => 'Shipping',
- '$balance_due_label' => 'Balance Due',
- '$outstanding_label' => 'Balance Due',
- '$partial_due_label' => 'Partial Due',
- '$quote.total_label' => 'Total',
- '$payment_due_label' => 'Payment due',
- '$credit.date_label' => 'Credit Date',
- '$invoiceDate_label' => 'Invoice Date',
- '$view_button_label' => 'View Invoice',
- '$client.city_label' => 'City',
- '$spc_qr_code_label' => '',
- '$client_name_label' => 'Client Name',
- '$client.name_label' => 'Client Name',
- '$paymentLink_label' => 'Pay Now',
- '$payment_url_label' => 'Pay Now',
- '$page_layout_label' => '',
- '$task.task1_label' => '',
- '$task.task2_label' => '',
- '$task.task3_label' => '',
- '$task.task4_label' => '',
- '$task.hours_label' => 'Hours',
- '$amount_due_label' => 'Amount due',
- '$amount_raw_label' => 'Amount',
- '$invoice_no_label' => 'Invoice Number',
- '$quote.date_label' => 'Quote Date',
- '$vat_number_label' => 'VAT Number',
- '$viewButton_label' => 'View Invoice',
- '$portal_url_label' => '',
- '$task.date_label' => 'Date',
- '$task.rate_label' => 'Rate',
- '$task.cost_label' => 'Rate',
- '$statement_label' => 'Statement',
- '$user_iban_label' => '',
- '$signature_label' => '',
- '$id_number_label' => 'ID Number',
- '$credit_no_label' => 'Invoice Number',
- '$font_size_label' => '',
- '$view_link_label' => 'View Invoice',
- '$page_size_label' => '',
- '$country_2_label' => 'Country',
- '$firstName_label' => 'First Name',
- '$user.name_label' => 'Name',
- '$font_name_label' => '',
- '$auto_bill_label' => '',
- '$payments_label' => 'Payments',
- '$task.tax_label' => 'Tax',
- '$discount_label' => 'Discount',
- '$subtotal_label' => 'Subtotal',
- '$company1_label' => '',
- '$company2_label' => '',
- '$company3_label' => '',
- '$company4_label' => '',
- '$due_date_label' => 'Due Date',
- '$poNumber_label' => 'PO Number',
- '$quote_no_label' => 'Quote Number',
- '$address2_label' => 'Apt/Suite',
- '$address1_label' => 'Street',
- '$viewLink_label' => 'View Invoice',
- '$autoBill_label' => '',
- '$view_url_label' => 'View Invoice',
- '$font_url_label' => '',
- '$details_label' => 'Details',
- '$balance_label' => 'Balance',
- '$partial_label' => 'Partial Due',
- '$client1_label' => '',
- '$client2_label' => '',
- '$client3_label' => '',
- '$client4_label' => '',
- '$dueDate_label' => 'Due Date',
- '$invoice_label' => 'Invoice Number',
- '$account_label' => 'Company Name',
- '$country_label' => 'Country',
- '$contact_label' => 'Name',
- '$app_url_label' => '',
- '$website_label' => 'Website',
- '$entity_label' => 'Invoice',
- '$thanks_label' => 'Thanks',
- '$amount_label' => 'Total',
- '$method_label' => 'Method',
- '$number_label' => 'Invoice Number',
- '$footer_label' => '',
- '$client_label' => 'Client Name',
- '$email_label' => 'Email',
- '$notes_label' => 'Public Notes',
- '_rate1_label' => 'Tax',
- '_rate2_label' => 'Tax',
- '_rate3_label' => 'Tax',
- '$taxes_label' => 'Taxes',
- '$total_label' => 'Total',
- '$phone_label' => 'Phone',
- '$terms_label' => 'Invoice Terms',
- '$from_label' => 'From',
- '$item_label' => 'Item',
- '$date_label' => 'Invoice Date',
- '$tax_label' => 'Tax',
- '$dir_label' => '',
- '$to_label' => 'To',
- '$show_paid_stamp_label' => '',
- '$status_logo_label' => '',
- '$show_shipping_address_label' => '',
- '$show_shipping_address_block_label' => '',
- '$show_shipping_address_visibility_label' => '',
- ],
+ 'labels' => $this->mockTranslatedLabels(),
];
}
+
+
+ private function mockTranslatedLabels()
+ {
+ return [
+ '$show_shipping_address_visibility_label' => ctrans('texts.shipping_address'),
+ '$client.shipping_postal_code_label' => ctrans('texts.shipping_postal_code'),
+ '$show_shipping_address_block_label' => ctrans('texts.shipping_address'),
+ '$client.billing_postal_code_label' => ctrans('texts.billing_postal_code'),
+ '$company.postal_city_state_label' => ctrans('texts.postal_city_state'),
+ '$company.city_state_postal_label' => ctrans('texts.city_state_postal'),
+ '$product.gross_line_total_label' => ctrans('texts.gross_line_total'),
+ '$client.shipping_address1_label' => ctrans('texts.shipping_address1'),
+ '$client.postal_city_state_label' => ctrans('texts.postal_city_state'),
+ '$client.shipping_address2_label' => ctrans('texts.shipping_address2'),
+ '$client.city_state_postal_label' => ctrans('texts.city_state_postal'),
+ '$client.billing_address2_label' => ctrans('texts.billing_address2'),
+ '$client.shipping_address_label' => ctrans('texts.shipping_address'),
+ '$client.billing_address1_label' => ctrans('texts.billing_address1'),
+ '$client.shipping_country_label' => ctrans('texts.shipping_country'),
+ '$invoiceninja.whitelabel_label' => ctrans('texts.white_label_link'),
+ '$client.billing_country_label' => ctrans('texts.billing_country'),
+ '$client.billing_address_label' => ctrans('texts.billing_address'),
+ '$task.gross_line_total_label' => ctrans('texts.gross_line_total'),
+ '$contact.portal_button_label' => ctrans('texts.button'),
+ '$client.shipping_state_label' => ctrans('texts.shipping_state'),
+ '$show_shipping_address_label' => ctrans('texts.show_shipping_address'),
+ '$invoice.public_notes_label' => ctrans('texts.public_notes'),
+ '$client.billing_state_label' => ctrans('texts.billing_state'),
+ '$client.shipping_city_label' => ctrans('texts.shipping_city'),
+ '$product.description_label' => ctrans('texts.description'),
+ '$product.product_key_label' => ctrans('texts.product_key'),
+ '$entity.public_notes_label' => ctrans('texts.public_notes'),
+ '$client.public_notes_label' => ctrans('texts.public_notes'),
+ '$company.postal_code_label' => ctrans('texts.postal_code'),
+ '$company.postal_city_label' => ctrans('texts.postal_city'),
+ '$secondary_font_name_label' => ctrans('texts.secondary_font'),
+ '$client.billing_city_label' => ctrans('texts.billing_city'),
+ '$invoice.balance_due_label' => ctrans('texts.balance_due'),
+ '$product.line_total_label' => ctrans('texts.line_total'),
+ '$product.tax_amount_label' => ctrans('texts.tax_amount'),
+ '$client.postal_code_label' => ctrans('texts.postal_code'),
+ '$company.vat_number_label' => ctrans('texts.vat_number'),
+ '$client.postal_city_label' => ctrans('texts.postal_city'),
+ '$quote.quote_number_label' => ctrans('texts.quote_number'),
+ '$invoice.invoice_no_label' => ctrans('texts.invoice_no'),
+ '$contact.first_name_label' => ctrans('texts.first_name'),
+ '$secondary_font_url_label' => ctrans('texts.secondary_font'),
+ '$contact.signature_label' => ctrans('texts.signature'),
+ '$product.tax_name1_label' => ctrans('texts.tax_name1'),
+ '$product.tax_name2_label' => ctrans('texts.tax_name2'),
+ '$product.tax_name3_label' => ctrans('texts.tax_name3'),
+ '$product.unit_cost_label' => ctrans('texts.unit_cost'),
+ '$company.id_number_label' => ctrans('texts.id_number'),
+ '$quote.valid_until_label' => ctrans('texts.valid_until'),
+ '$invoice_total_raw_label' => ctrans('texts.invoice_total'),
+ '$client.vat_number_label' => ctrans('texts.vat_number'),
+ '$company_logo_size_label' => ctrans('texts.logo'),
+ '$postal_city_state_label' => ctrans('texts.postal_city_state'),
+ '$invoice.po_number_label' => ctrans('texts.po_number'),
+ '$contact.last_name_label' => ctrans('texts.last_name'),
+ '$contact.full_name_label' => ctrans('texts.full_name'),
+ '$city_state_postal_label' => ctrans('texts.city_state_postal'),
+ '$company.country_2_label' => ctrans('texts.country'),
+ '$custom_surcharge1_label' => ctrans('texts.custom_surcharge1'),
+ '$custom_surcharge2_label' => ctrans('texts.custom_surcharge2'),
+ '$custom_surcharge3_label' => ctrans('texts.custom_surcharge3'),
+ '$custom_surcharge4_label' => ctrans('texts.custom_surcharge4'),
+ '$quote.balance_due_label' => ctrans('texts.balance_due'),
+ '$product.product1_label' => ctrans('texts.product1'),
+ '$product.product2_label' => ctrans('texts.product2'),
+ '$product.product3_label' => ctrans('texts.product3'),
+ '$product.product4_label' => ctrans('texts.product4'),
+ '$statement_amount_label' => ctrans('texts.amount'),
+ '$task.description_label' => ctrans('texts.description'),
+ '$product.discount_label' => ctrans('texts.discount'),
+ '$product.quantity_label' => ctrans('texts.quantity'),
+ '$entity_issued_to_label' => ctrans('texts.quote_issued_to'),
+ '$partial_due_date_label' => ctrans('texts.partial_due_date'),
+ '$invoice.datetime_label' => ctrans('texts.datetime_format_id'),
+ '$invoice.due_date_label' => ctrans('texts.due_date'),
+ '$company.address1_label' => ctrans('texts.address1'),
+ '$company.address2_label' => ctrans('texts.address2'),
+ '$total_tax_labels_label' => ctrans('texts.total_taxes'),
+ '$total_tax_values_label' => ctrans('texts.total_taxes'),
+ '$credit.po_number_label' => ctrans('texts.po_number'),
+ '$client.id_number_label' => ctrans('texts.id_number'),
+ '$credit.credit_note_label' => ctrans('texts.credit_note'),
+ '$assigned_to_user_label' => ctrans('texts.assigned_to'),
+ '$invoice.discount_label' => ctrans('texts.discount'),
+ '$invoice.subtotal_label' => ctrans('texts.subtotal'),
+ '$contact.custom1_label' => ctrans('texts.custom1'),
+ '$contact.custom2_label' => ctrans('texts.custom2'),
+ '$contact.custom3_label' => ctrans('texts.custom3'),
+ '$contact.custom4_label' => ctrans('texts.custom4'),
+ '$task.line_total_label' => ctrans('texts.line_total'),
+ '$task.tax_amount_label' => ctrans('texts.tax_amount'),
+ '$line_tax_labels_label' => ctrans('texts.line_taxes'),
+ '$line_tax_values_label' => ctrans('texts.line_taxes'),
+ '$invoice.custom1_label' => ctrans('texts.custom1'),
+ '$invoice.custom2_label' => ctrans('texts.custom2'),
+ '$invoice.custom3_label' => ctrans('texts.custom3'),
+ '$invoice.custom4_label' => ctrans('texts.custom4'),
+ '$company.custom1_label' => ctrans('texts.custom1'),
+ '$company.custom2_label' => ctrans('texts.custom2'),
+ '$company.custom3_label' => ctrans('texts.custom3'),
+ '$company.custom4_label' => ctrans('texts.custom4'),
+ '$secondary_color_label' => ctrans('texts.secondary_color'),
+ '$balance_due_raw_label' => ctrans('texts.balance_due'),
+ '$entity.datetime_label' => ctrans('texts.datetime_format_id'),
+ '$credit.datetime_label' => ctrans('texts.datetime_format_id'),
+ '$client.address2_label' => ctrans('texts.address2'),
+ '$company.address_label' => ctrans('texts.address'),
+ '$client.address1_label' => ctrans('texts.address1'),
+ '$quote.po_number_label' => ctrans('texts.po_number'),
+ '$client.currency_label' => ctrans('texts.currency'),
+ '$user.first_name_label' => ctrans('texts.first_name'),
+ '$created_by_user_label' => ctrans('texts.created_by'),
+ '$company.country_label' => ctrans('texts.country'),
+ '$tech_hero_image_label' => '',
+ '$company.website_label' => ctrans('texts.website'),
+ '$invoice.balance_label' => ctrans('texts.balance'),
+ '$client.country_label' => ctrans('texts.country'),
+ '$task.tax_name1_label' => ctrans('texts.tax_name1'),
+ '$task.tax_name2_label' => ctrans('texts.tax_name2'),
+ '$task.tax_name3_label' => ctrans('texts.tax_name3'),
+ '$payment_button_label' => '',
+ '$credit.custom1_label' => ctrans('texts.custom1'),
+ '$credit.custom2_label' => ctrans('texts.custom2'),
+ '$credit.custom3_label' => ctrans('texts.custom3'),
+ '$credit.custom4_label' => ctrans('texts.custom4'),
+ '$emailSignature_label' => ctrans('texts.email_signature'),
+ '$quote.datetime_label' => ctrans('texts.datetime_format_id'),
+ '$client.custom1_label' => ctrans('texts.custom1'),
+ '$client_address_label' => ctrans('texts.address'),
+ '$client.address_label' => ctrans('texts.address'),
+ '$payment_qrcode_label' => '',
+ '$client.custom2_label' => ctrans('texts.custom2'),
+ '$invoice.number_label' => ctrans('texts.number'),
+ '$quote.quote_no_label' => ctrans('texts.quote_no'),
+ '$user.last_name_label' => ctrans('texts.last_name'),
+ '$client.custom3_label' => ctrans('texts.custom3'),
+ '$client.website_label' => ctrans('texts.website'),
+ '$dir_text_align_label' => '',
+ '$client.custom4_label' => ctrans('texts.custom4'),
+ '$client.balance_label' => ctrans('texts.balance'),
+ '$client_balance_label' => ctrans('texts.client_balance'),
+ '$credit.balance_label' => ctrans('texts.balance'),
+ '$credit_balance_label' => ctrans('texts.credit_balance'),
+ '$gross_subtotal_label' => ctrans('texts.subtotal'),
+ '$invoice.amount_label' => ctrans('texts.amount'),
+ '$entity_footer_label' => ctrans('texts.footer'),
+ '$entity_images_label' => '',
+ '$task.discount_label' => ctrans('texts.discount'),
+ '$portal_button_label' => '',
+ '$approveButton_label' => ctrans('texts.approve'),
+ '$quote.custom1_label' => ctrans('texts.custom1'),
+ '$quote.custom2_label' => ctrans('texts.custom2'),
+ '$quote.custom3_label' => ctrans('texts.custom3'),
+ '$quote.custom4_label' => ctrans('texts.custom4'),
+ '$company.email_label' => ctrans('texts.email'),
+ '$primary_color_label' => ctrans('texts.primary_color'),
+ '$company.phone_label' => ctrans('texts.phone'),
+ '$exchange_rate_label' => ctrans('texts.exchange_rate'),
+ '$client.number_label' => ctrans('texts.number'),
+ '$global_margin_label' => '',
+ '$contact.phone_label' => ctrans('texts.phone'),
+ '$company.state_label' => ctrans('texts.state'),
+ '$credit.number_label' => ctrans('texts.number'),
+ '$entity_number_label' => ctrans('texts.quote_number'),
+ '$credit_number_label' => ctrans('texts.credit_number'),
+ '$client.lang_2_label' => ctrans('texts.language'),
+ '$contact.email_label' => ctrans('texts.email'),
+ '$invoice.taxes_label' => ctrans('texts.taxes'),
+ '$credit_amount_label' => ctrans('texts.credit_amount'),
+ '$invoice.total_label' => ctrans('texts.total'),
+ '$product.date_label' => ctrans('texts.date'),
+ '$product.item_label' => ctrans('texts.item'),
+ '$public_notes_label' => ctrans('texts.public_notes'),
+ '$entity.terms_label' => ctrans('texts.terms'),
+ '$task.service_label' => ctrans('texts.service'),
+ '$portalButton_label' => '',
+ '$payment.date_label' => ctrans('texts.date'),
+ '$client.phone_label' => ctrans('texts.phone'),
+ '$invoice.date_label' => ctrans('texts.date'),
+ '$client.state_label' => ctrans('texts.state'),
+ '$number_short_label' => '',
+ '$quote.number_label' => ctrans('texts.number'),
+ '$contact.name_label' => ctrans('texts.name'),
+ '$company.city_label' => ctrans('texts.city'),
+ '$company.name_label' => ctrans('texts.name'),
+ '$company.logo_label' => ctrans('texts.logo'),
+ '$company_logo_label' => ctrans('texts.logo'),
+ '$payment_link_label' => ctrans('texts.link'),
+ '$client.email_label' => ctrans('texts.email'),
+ '$paid_to_date_label' => ctrans('texts.paid_to_date'),
+ '$net_subtotal_label' => ctrans('texts.net_subtotal'),
+ '$credit.total_label' => ctrans('texts.total'),
+ '$quote.amount_label' => ctrans('texts.amount'),
+ '$description_label' => ctrans('texts.description'),
+ '$product.tax_label' => ctrans('texts.tax'),
+ '$your_entity_label' => ctrans("texts.your_{$this->entity_string}"),
+ '$view_button_label' => ctrans('texts.view'),
+ '$status_logo_label' => ctrans('texts.logo'),
+ '$credit.date_label' => ctrans('texts.date'),
+ '$payment_due_label' => ctrans('texts.payment_due'),
+ '$invoiceDate_label' => ctrans('texts.date'),
+ '$valid_until_label' => ctrans('texts.valid_until'),
+ '$postal_city_label' => ctrans('texts.postal_city'),
+ '$client_name_label' => ctrans('texts.client_name'),
+ '$client.name_label' => ctrans('texts.name'),
+ '$spc_qr_code_label' => '',
+ '$client.city_label' => ctrans('texts.city'),
+ '$paymentLink_label' => ctrans('texts.link'),
+ '$payment_url_label' => ctrans('texts.url'),
+ '$page_layout_label' => ctrans('texts.page_layout'),
+ '$balance_due_label' => ctrans('texts.balance_due'),
+ '$outstanding_label' => ctrans('texts.outstanding'),
+ '$partial_due_label' => ctrans('texts.partial_due'),
+ '$quote.total_label' => ctrans('texts.total'),
+ '$task.task1_label' => ctrans('texts.task1'),
+ '$task.task2_label' => ctrans('texts.task2'),
+ '$task.task3_label' => ctrans('texts.task3'),
+ '$task.task4_label' => ctrans('texts.task4'),
+ '$task.hours_label' => ctrans('texts.hours'),
+ '$viewButton_label' => ctrans('texts.view'),
+ '$quote.date_label' => ctrans('texts.date'),
+ '$amount_raw_label' => ctrans('texts.amount'),
+ '$vat_number_label' => ctrans('texts.vat_number'),
+ '$invoice_no_label' => ctrans('texts.invoice_no'),
+ '$portal_url_label' => ctrans('texts.url'),
+ '$amount_due_label' => ctrans('texts.amount_due'),
+ '$country_2_label' => ctrans('texts.country'),
+ '$task.date_label' => ctrans('texts.date'),
+ '$task.rate_label' => ctrans('texts.rate'),
+ '$task.cost_label' => ctrans('texts.cost'),
+ '$statement_label' => ctrans('texts.statement'),
+ '$view_link_label' => ctrans('texts.link'),
+ '$user_iban_label' => ctrans('texts.iban'),
+ '$signature_label' => ctrans('texts.signature'),
+ '$font_size_label' => ctrans('texts.font_size'),
+ '$po_number_label' => ctrans('texts.po_number'),
+ '$page_size_label' => ctrans('texts.page_size'),
+ '$user.name_label' => ctrans('texts.name'),
+ '$id_number_label' => ctrans('texts.id_number'),
+ '$credit_no_label' => ctrans('texts.credit_note'),
+ '$firstName_label' => ctrans('texts.first_name'),
+ '$font_name_label' => '',
+ '$auto_bill_label' => ctrans('texts.auto_bill'),
+ '$payments_label' => ctrans('texts.payments'),
+ '$shipping_label' => '',
+ '$task.tax_label' => ctrans('texts.tax'),
+ '$viewLink_label' => ctrans('texts.link'),
+ '$company1_label' => ctrans('texts.company1'),
+ '$company2_label' => ctrans('texts.company2'),
+ '$company3_label' => ctrans('texts.company3'),
+ '$company4_label' => ctrans('texts.company4'),
+ '$due_date_label' => ctrans('texts.due_date'),
+ '$address2_label' => ctrans('texts.address2'),
+ '$address1_label' => ctrans('texts.address1'),
+ '$poNumber_label' => ctrans('texts.po_number'),
+ '$quote_no_label' => ctrans('texts.quote_no'),
+ '$autoBill_label' => ctrans('texts.auto_bill'),
+ '$view_url_label' => ctrans('texts.url'),
+ '$font_url_label' => '',
+ '$discount_label' => ctrans('texts.discount'),
+ '$subtotal_label' => ctrans('texts.subtotal'),
+ '$country_label' => ctrans('texts.country'),
+ '$details_label' => ctrans('texts.details'),
+ '$custom1_label' => ctrans('texts.custom1'),
+ '$custom2_label' => ctrans('texts.custom2'),
+ '$custom3_label' => ctrans('texts.custom3'),
+ '$custom4_label' => ctrans('texts.custom4'),
+ '$dueDate_label' => ctrans('texts.due_date'),
+ '$client1_label' => ctrans('texts.client1'),
+ '$client2_label' => ctrans('texts.client2'),
+ '$contact_label' => ctrans('texts.contact'),
+ '$account_label' => '',
+ '$client3_label' => ctrans('texts.client3'),
+ '$app_url_label' => ctrans('texts.url'),
+ '$website_label' => ctrans('texts.website'),
+ '$client4_label' => ctrans('texts.client4'),
+ '$balance_label' => ctrans('texts.balance'),
+ '$partial_label' => ctrans('texts.partial'),
+ '$footer_label' => ctrans('texts.footer'),
+ '$entity_label' => ctrans("texts.{$this->entity_string}"),
+ '$thanks_label' => ctrans('texts.thanks'),
+ '$method_label' => ctrans('texts.method'),
+ '$client_label' => ctrans('texts.client'),
+ '$number_label' => ctrans('texts.number'),
+ '$amount_label' => ctrans('texts.amount'),
+ '$notes_label' => ctrans('texts.notes'),
+ '$terms_label' => ctrans('texts.terms'),
+ 'tax_rate1_label' => ctrans('texts.tax_rate1'),
+ 'tax_rate2_label' => ctrans('texts.tax_rate2'),
+ 'tax_rate3_label' => ctrans('texts.tax_rate3'),
+ '$phone_label' => ctrans('texts.phone'),
+ '$email_label' => ctrans('texts.email'),
+ '$taxes_label' => ctrans('texts.taxes'),
+ '$total_label' => ctrans('texts.total'),
+ '$from_label' => ctrans('texts.from'),
+ '$item_label' => ctrans('texts.item'),
+ '$date_label' => ctrans('texts.date'),
+ '$tax_label' => ctrans('texts.tax'),
+ '$dir_label' => '',
+ '$to_label' => ctrans('texts.to'),
+ ];
+ }
+
+ private function getVendorStubVariables()
+ {
+ return ['values' => [
+ '$vendor.billing_postal_code' => '06270-5526',
+ '$company.postal_city_state' => '29359 New Loy, Delaware',
+ '$company.city_state_postal' => 'New Loy, Delaware 29359',
+ '$product.gross_line_total' => '',
+ '$purchase_order.po_number' => 'PO12345',
+ '$vendor.postal_city_state' => '06270-5526 Jameyhaven, West Virginia',
+ '$vendor.city_state_postal' => 'Jameyhaven, West Virginia 06270-5526',
+ '$purchase_order.due_date' => '02-12-2021',
+ '$vendor.billing_address1' => '589',
+ '$vendor.billing_address2' => '761 Odessa Centers Suite 673',
+ '$invoiceninja.whitelabel' => 'https://raw.githubusercontent.com/invoiceninja/invoiceninja/v5-develop/public/images/new_logo.png',
+ '$purchase_order.custom1' => 'Custom 1',
+ '$purchase_order.custom2' => 'Custom 2',
+ '$purchase_order.custom3' => 'Custom 3',
+ '$purchase_order.custom4' => 'Custom 4',
+ '$vendor.billing_address' => '589
761 Odessa Centers Suite 673
New Loy, Delaware 29359
Afghanistan
',
+ '$vendor.billing_country' => 'Afghanistan',
+ '$purchase_order.number' => 'Live Preview #790',
+ '$purchase_order.total' => '$10,256.40',
+ '$vendor.billing_state' => 'West Virginia',
+ '$product.description' => '',
+ '$product.product_key' => '',
+ '$entity.public_notes' => 'These are public notes for the Vendor',
+ '$purchase_order.date' => '14/Mar/2023',
+ '$company.postal_code' => '29359',
+ '$company.postal_city' => '29359 New Loy',
+ '$vendor.billing_city' => 'Jameyhaven',
+ '$vendor.public_notes' => 'Public notes',
+ '$product.line_total' => '',
+ '$product.tax_amount' => '',
+ '$vendor.postal_code' => '06270-5526',
+ '$vendor.postal_city' => '06270-5526 Jameyhaven',
+ '$contact.first_name' => 'Geo',
+ '$company.vat_number' => 'vat number',
+ '$contact.signature' => '',
+ '$product.tax_name1' => '',
+ '$product.tax_name2' => '',
+ '$product.tax_name3' => '',
+ '$product.unit_cost' => '',
+ '$custom_surcharge1' => '$0.00',
+ '$custom_surcharge2' => '$0.00',
+ '$custom_surcharge3' => '$0.00',
+ '$custom_surcharge4' => '$0.00',
+ '$postal_city_state' => '06270-5526 Jameyhaven, West Virginia',
+ '$company_logo_size' => '65%',
+ '$vendor.vat_number' => 'At qui autem iusto et.',
+ '$contact.full_name' => 'Geo Maggio',
+ '$city_state_postal' => 'Jameyhaven, West Virginia 06270-5526',
+ '$contact.last_name' => 'Maggio',
+ '$company.country_2' => 'US',
+ '$company.id_number' => 'id number',
+ '$product.product1' => '',
+ '$product.product2' => '',
+ '$product.product3' => '',
+ '$product.product4' => '',
+ '$statement_amount' => '',
+ '$product.discount' => '',
+ '$assigned_to_user' => '',
+ '$entity_issued_to' => '',
+ '$product.quantity' => '',
+ '$total_tax_labels' => '',
+ '$total_tax_values' => '',
+ '$partial_due_date' => ' ',
+ '$company.address2' => '70218 Lori Station Suite 529',
+ '$company.address1' => 'Christiansen Garden',
+ '$vendor.id_number' => 'Libero debitis.',
+ '$contact.custom1' => null,
+ '$contact.custom2' => null,
+ '$contact.custom3' => null,
+ '$contact.custom4' => null,
+ '$secondary_color' => '#7081e0',
+ '$company.custom1' => ' ',
+ '$company.custom2' => ' ',
+ '$company.custom3' => ' ',
+ '$company.custom4' => ' ',
+ '$balance_due_raw' => '10256.40',
+ '$entity.datetime' => '14/Mar/2023 10:43 pm',
+ '$vendor.address1' => '589',
+ '$vendor.address2' => '761 Odessa Centers Suite 673',
+ '$line_tax_values' => '$488.40',
+ '$line_tax_labels' => 'Sales Tax 5%',
+ '$company.address' => 'Christiansen Garden
70218 Lori Station Suite 529
New Loy, Delaware 29359
United States
Phone: 1-240-886-2233
Email: immanuel53@example.net
',
+ '$user.first_name' => 'Mr. Louvenia Armstrong',
+ '$created_by_user' => 'Mr. Louvenia Armstrong Prof. Reyes Anderson',
+ '$vendor.currency' => 'USD',
+ '$company.country' => 'United States',
+ '$tech_hero_image' => 'http://ninja.test:8000/images/pdf-designs/tech-hero-image.jpg',
+ '$company.website' => 'http://www.dare.com/vero-consequatur-eveniet-dolorum-exercitationem-alias-repellat.html',
+ '$gross_subtotal' => '$10,256.40',
+ '$emailSignature' => ' ',
+ '$vendor_address' => '589
761 Odessa Centers Suite 673
New Loy, Delaware 29359
Afghanistan
',
+ '$vendor.address' => '589
761 Odessa Centers Suite 673
New Loy, Delaware 29359
Afghanistan
',
+ '$vendor.country' => 'Afghanistan',
+ '$vendor.custom3' => 'Ea quia tempore.',
+ '$vendor.custom1' => 'Necessitatibus aut.',
+ '$vendor.custom4' => 'Nobis aut harum.',
+ '$user.last_name' => 'Prof. Reyes Anderson',
+ '$vendor.custom2' => 'Sit fuga quas sint.',
+ '$vendor.website' => 'http://abernathy.com/consequatur-at-beatae-nesciunt',
+ '$dir_text_align' => 'left',
+ '$entity_footer' => '',
+ '$entity_images' => '',
+ '$contact.email' => '',
+ '$primary_color' => '#298AAB',
+ '$contact.phone' => '+1 (920) 735-1990',
+ '$vendor.number' => '0001',
+ '$company.phone' => '1-240-886-2233',
+ '$global_margin' => '6.35mm',
+ '$company.state' => 'Delaware',
+ '$entity_number' => 'Live Preview #790',
+ '$company.email' => 'immanuel53@example.net',
+ '$product.date' => '',
+ '$vendor.email' => '',
+ '$entity.terms' => '',
+ '$product.item' => '',
+ '$public_notes' => null,
+ '$paid_to_date' => '$0.00',
+ '$net_subtotal' => '$9,768.00',
+ '$payment.date' => ' ',
+ '$vendor.phone' => ' ',
+ '$contact.name' => 'Geo Maggio',
+ '$number_short' => 'Live Preview #790',
+ '$company.name' => 'Mrs. Kristina Powlowski',
+ '$company.city' => 'New Loy',
+ '$vendor.state' => 'West Virginia',
+ '$company.logo' => 'data:image/png;base64, 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',
+ '$company_logo' => 'data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wwDEggpbTAjzwAAChlJREFUeNrlm3t0VNUVxn93XnlVTCSTRIoI5ZV0Bq8GYhDFEiAxsHyglacKbZxgsS0PKwVabdeCILhcaHgY8A1CosiiiKiJYAihBgUM8cq94VFTDQKBJKtJFoQhM7kz/WMy00kgk8nTJP3+mTUz9557vu/uc87e++wj0AUQzaZYIBaIAQYBUUAoENBwiRWoAsqAfwPFQKEkK0pn903oJMI3A1OAx4Ex7WxuH7AN2C3JSo1oNiHJSvcUQDSbJgNpwB2d9MI+B5ZLsvLPbiWAaDZNB7IADV2DGmCqJCv72msRmjYSdn+OEs0mG/B+F5IHuBHYK5pN5cBQ7z51iQWIZlMwsAcYT/fAa8DvAbW11qBpw1v/BVDbjcgDPAXYgfDWWoPGX/KSrCCaTY8DJXRPCECFaDbd2dDXjhHAi/wGYCvdH4dFs2mOvyJo/ST/AfAbeg6mREVEVEuyclg0m7hYXtF6AZq8+Z5E3o3kqIiIf0myctyXCEILFvB4DzF7X7hDkpVvWr0MimbTYOA7egduAqqut0QKzSx3wQ1LXW9BHRAEOJuKcM0ccLG8gqiIiJyGqK23QAcYJFnJ9ccCRgFH6Z24FTjjbQXX8wMO0Xuxt+kQEK4T1b1P70Y8cMQtRFMLyKL3I9PbCoQmyYxP+P+ACHwryQo6rx/TfN1hNBq5evUq9fX13Xu61+kICAigsrLS12ULJVlJ8VhAQw7vvK87wsPDSRg/nnEJCdx9z9huSf6rLw+Rn3eAvLz9XLhwocXoUZIVjwDzgAx/HlJfX4/VakUURZInTWbM3XdjHjHiJyF8oriYQ4cK+Cw7m8LCQoKCgtDpXEbtdDoRBJ+e/mQg2y1AAT6yt1qtlgEDBlBSUoJGo0Gj0TBo0CCKi4tRVRWNRkNy8iQmTZ7M7bGxREZGdgrhysoKio4V8Vl2NtmffoK9vh6NRkNMTAxnzpxBVVWX21dXxxOzZ/PB9u2+mntTkpVUtwBOX1caDAaOFB5jU0YGGa9uQBAEoiIjWZOe7upQTg6KIlNXV4fdbifCaGT6zJmMSxhPdHQ0hoCANhG22+2cOnmSg/n5bH/vPc6XnUev16PX6zGZTExMTGT06LtYtnQJP5SW4lBVhg0bxutvvkXf8HBuH2H21XyNJCuhgmg2jQS+9kcAt2nNmj4dWT6O3W7HkprKs39eAkB+Xh5ZWZmcPHGCyspKBEHgqtXKnfHxPPbEbERRpP8tt/gkff7cOY4fP07Wtq0UFBQQGBgIQFhYGMOHD2fGzFlMTEoC4NX161mb/gpBwcHodToWLFzEE3PmeNpqQQCAMEE0myzAG/4K4MZHu3aRlrYCm82GWl9P5vvbuU0UPf9vfy+LFcuXNxqTdrsdVVWZPmMG02bMoE+fPoDA5cuX2LljB1mZmZ7nucevqqosWvQMKampnrZPnjjBzOnTcDqdOBwO4kbF8daWLdf02w8BJgii2bQGeKa1AriRMmc2hYWFqKrKuIQEXk5fy84dO0hbsRy9Xu8hHxMTw6VLl6ioqMBqtSIIAg6Hw+WNaTSe5xiNRoxGI5IkNRJh/vwF/NZi4blly9iz5yO0Wi1hYTexcdMmfmm+PlE/BPiDIJpN/wAebqsAAMe/lZhrsXDlyhXUepW+4X2pqalpdM3P+/fnk+wcAAqPHiUn+1NOnz4NTieDhwwlKfk+Rt/lmoenPPgAP3z/faP7Q0JCuHzpEoJGgyAITJ02jb8897xPdn4IsEaHa6OyXRhxm8iXR46ydPFicnKyryHvJuDGyLg4RsbFNdtenxtuuOa32tpaEAT69+/P7j0fo9XpOmJhidLg2qXtEKx+6SVy8w40Iuv2zrZl+h9mvJuZ5Zn8PHDCuvUb+Dg7p6PIA4Rq+N8WdYegb3g4BV8dZv6CBZ4xHhgYhN1u97sNm81GUFCQ53tCQgLfyDK/SkjoaNcioNP281IsqRwpPEZkZASXL19izOh4/rp0actWtHIlcbF3UFVVhV6vZ1/ufl5Zt77TvElBNJtkwNSeSbAlfHHwIJYnUwgMDCQoKIjo6GgURcHhcOBsWAWGDhlCaWkptbW12Gw20la+wCOPPtoucn5Mgnt0uCozOhX33Hsv+w/kM3G8y4SLiopc/kOD66rVaikuLvaY/+49HzN02LCuCCeqNbjKUjo1Qht371iSkxLR6/XU19ez+sUXKfxG4tR3JZz6roRj0rekr12Lqqro9Xp+/fAUxo65iw937uxsAc4Lotm0GljS0UPg9Y0b2bZtK1VVVTgcDqKiosjYtIlhw6N9RmklJSX8cd48Ss+UotVqCQkJYcKEiaStWtUZQ+B3gmg2zQa2dIwATpYsXsyBvDysVitWq5X777+fxUuX0a9fv1Z1/kJZGevSX2HHjh0EBwej0WgYYR7BytWruGXArR0lwFhBNJtMgNweAc79eJa//+05jh49itPpxGaz8adnF/PotKmEhd3ULhutqalh965drF71AjqdDlVVGTx4MKlPPcUDDz7UXgGCWhUON8XBAwfYsH49iiJ7Ira/Pv88iUn3eYKgjoLD4WB/7uesXLGC8vJyBEEgNDSUBx56iCVLl7VFgDJJVvq5BdgLJPorwLubN7Nt67ucO3cOu91OfHw88xcsJC4+vksyQUXHjrFh3ToOHswnODgYVVVJTEpi4aJnuHXgQH8FSJdkZZFbAJ/zgMFg4NDhI6S/vIaszEzsdhtW61VmzXqMFIuFgYN+ml20sz/+yOa332bL5ncIDgnBZqsjNnYkKU9amJCY2JIA44B8twA3AtXNekuCQG1tLQaDAYPBgCV1Lpa5qRgMAXQHOFSVt996kzdee43aK1dwOp0YjUaqq5ulhCQrQtN9gX3AxOZuGDhwIPOefpr7Jk3u1mnxvNxcNmZkcPr0KZ9hsCQrz3qnxQHGAgd9ZYNtNhs9Ae68oQ8MAUo8aXEvK6jGVYTYm1Egyco97i9No8Gp9H7M9a4eu159QDlg7KXkcyVZaTTPNR0C4Kq9Pd1LBYgEyr13hxuVyFwsr+BiecV/oiIibgZG9TLyFklWvmhaLtdckZQWV+2t0EvIn5Vk5bo7Mtcl2CBCOFDRSwT4GVDrV5lcEyHuBA73cPL9JFlpNumj8UEeSVaO0DPLZN0YJclKma+i6WZrhS+WV7hFkKIiIqqB5B5GfpwkK1+2dKSmxUnOq2h6FpDZg958oT/nifya5b1EuB0o6glj3t/DVH4vc14ihOHKJAd0M+JngWhJVmpbc5LM750hrwarcBUer+pG5C0N63xtk752nAU0Yw0DgL3A8J/KtwdmSbJS3tYG2rQ36KXwGUlWonGVn3bl2YICwNQQ2JS3p6F2bY56CXFEkpWhuCow3+lE4muAIQ3xfHFrzb3DhoAfQwNgEvBIQ46hrUmWMmA78KEkK/nez+godFqw491R0WwKpeXj89W4qlW/BxTga0lWrnYGaW/8Fz1Q/ijOyWeJAAAAAElFTkSuQmCC',
+ '$description' => '',
+ '$product.tax' => '',
+ '$view_button' => '
+
+ ',
+ '$status_logo' => ' ',
+ '$partial_due' => '$0.00',
+ '$balance_due' => '$10,256.40',
+ '$outstanding' => '$10,256.40',
+ '$payment_due' => ' ',
+ '$postal_city' => '06270-5526 Jameyhaven',
+ '$vendor_name' => 'Claudie Nikolaus MD',
+ '$vendor.name' => 'Claudie Nikolaus MD',
+ '$vendor.city' => 'Jameyhaven',
+ '$page_layout' => 'portrait',
+ '$viewButton' => '
+
+ ',
+ '$amount_due' => '$10,256.40',
+ '$amount_raw' => '10256.40',
+ '$vat_number' => 'At qui autem iusto et.',
+ '$portal_url' => 'http://ninja.test:8000/vendor/',
+ '$po_number' => null,
+ '$statement' => '',
+ '$view_link' => '
+
+ ',
+ '$signature' => ' ',
+ '$font_size' => '16px',
+ '$page_size' => 'A4',
+ '$country_2' => 'AF',
+ '$firstName' => 'Geo',
+ '$id_number' => 'Libero debitis.',
+ '$user.name' => 'Mr. Louvenia Armstrong Prof. Reyes Anderson',
+ '$font_name' => 'Roboto',
+ '$auto_bill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
+ '$poNumber' => null,
+ '$payments' => '',
+ '$viewLink' => '
+
+ ',
+ '$subtotal' => '$9,768.00',
+ '$company1' => ' ',
+ '$company2' => ' ',
+ '$company3' => ' ',
+ '$company4' => ' ',
+ '$due_date' => ' ',
+ '$discount' => 0.0,
+ '$address1' => '589',
+ '$address2' => '761 Odessa Centers Suite 673',
+ '$autoBill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
+ '$view_url' => 'http://ninja.test:8000/vendor/purchase_order/OwH1Bkl0AP3EBQxJpGvEsU7YbTk5durD',
+ '$font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
+ '$details' => '',
+ '$balance' => '$0.00',
+ '$partial' => '$0.00',
+ '$custom1' => ' ',
+ '$custom2' => ' ',
+ '$custom3' => ' ',
+ '$custom4' => ' ',
+ '$dueDate' => ' ',
+ '$country' => 'Afghanistan',
+ '$vendor3' => 'Ea quia tempore.',
+ '$contact' => 'Geo Maggio',
+ '$account' => 'Mrs. Kristina Powlowski',
+ '$vendor1' => 'Necessitatibus aut.',
+ '$vendor4' => 'Nobis aut harum.',
+ '$vendor2' => 'Sit fuga quas sint.',
+ '$website' => 'http://abernathy.com/consequatur-at-beatae-nesciunt',
+ '$app_url' => 'http://ninja.test:8000',
+ '$footer' => '',
+ '$entity' => '',
+ '$thanks' => '',
+ '$amount' => '$10,256.40',
+ '$method' => ' ',
+ '$vendor' => 'Claudie Nikolaus MD',
+ '$number' => 'Live Preview #790',
+ '$email' => '',
+ '$terms' => '',
+ '$notes' => null,
+ '$tax_rate1' => '',
+ '$tax_rate2' => '',
+ '$tax_rate3' => '',
+ '$total' => '$10,256.40',
+ '$taxes' => '$488.40',
+ '$phone' => ' ',
+ '$from' => '',
+ '$item' => '',
+ '$date' => '14/Mar/2023',
+ '$tax' => '',
+ '$dir' => 'ltr',
+ '$to' => '',
+ ],
+ 'labels' => $this->vendorLabels(),
+];
+ }
+
+ private function vendorLabels()
+ {
+ return [
+ '$vendor.billing_postal_code_label' => ctrans('texts.billing_postal_code'),
+ '$company.postal_city_state_label' => ctrans('texts.postal_city_state'),
+ '$company.city_state_postal_label' => ctrans('texts.city_state_postal'),
+ '$product.gross_line_total_label' => ctrans('texts.gross_line_total'),
+ '$purchase_order.po_number_label' => ctrans('texts.po_number'),
+ '$vendor.postal_city_state_label' => ctrans('texts.postal_city_state'),
+ '$vendor.city_state_postal_label' => ctrans('texts.city_state_postal'),
+ '$purchase_order.due_date_label' => ctrans('texts.due_date'),
+ '$vendor.billing_address1_label' => ctrans('texts.billing_address1'),
+ '$vendor.billing_address2_label' => ctrans('texts.billing_address2'),
+ '$invoiceninja.whitelabel_label' => ctrans('texts.white_label'),
+ '$purchase_order.custom1_label' => ctrans('texts.custom1'),
+ '$purchase_order.custom2_label' => ctrans('texts.custom2'),
+ '$purchase_order.custom3_label' => ctrans('texts.custom3'),
+ '$purchase_order.custom4_label' => ctrans('texts.custom4'),
+ '$vendor.billing_address_label' => ctrans('texts.billing_address'),
+ '$vendor.billing_country_label' => ctrans('texts.billing_country'),
+ '$purchase_order.number_label' => ctrans('texts.number'),
+ '$purchase_order.total_label' => ctrans('texts.total'),
+ '$vendor.billing_state_label' => ctrans('texts.billing_state'),
+ '$product.description_label' => ctrans('texts.description'),
+ '$product.product_key_label' => ctrans('texts.product_key'),
+ '$entity.public_notes_label' => ctrans('texts.public_notes'),
+ '$purchase_order.date_label' => ctrans('texts.date'),
+ '$company.postal_code_label' => ctrans('texts.postal_code'),
+ '$company.postal_city_label' => ctrans('texts.postal_city'),
+ '$vendor.billing_city_label' => ctrans('texts.billing_city'),
+ '$vendor.public_notes_label' => ctrans('texts.public_notes'),
+ '$product.line_total_label' => ctrans('texts.line_total'),
+ '$product.tax_amount_label' => ctrans('texts.tax_amount'),
+ '$vendor.postal_code_label' => ctrans('texts.postal_code'),
+ '$vendor.postal_city_label' => ctrans('texts.postal_city'),
+ '$contact.first_name_label' => ctrans('texts.first_name'),
+ '$company.vat_number_label' => ctrans('texts.vat_number'),
+ '$contact.signature_label' => ctrans('texts.signature'),
+ '$product.tax_name1_label' => ctrans('texts.tax_name1'),
+ '$product.tax_name2_label' => ctrans('texts.tax_name2'),
+ '$product.tax_name3_label' => ctrans('texts.tax_name3'),
+ '$product.unit_cost_label' => ctrans('texts.unit_cost'),
+ '$custom_surcharge1_label' => ctrans('texts.custom_surcharge1'),
+ '$custom_surcharge2_label' => ctrans('texts.custom_surcharge2'),
+ '$custom_surcharge3_label' => ctrans('texts.custom_surcharge3'),
+ '$custom_surcharge4_label' => ctrans('texts.custom_surcharge4'),
+ '$postal_city_state_label' => ctrans('texts.postal_city_state'),
+ '$company_logo_size_label' => ctrans('texts.logo'),
+ '$vendor.vat_number_label' => ctrans('texts.vat_number'),
+ '$contact.full_name_label' => ctrans('texts.full_name'),
+ '$city_state_postal_label' => ctrans('texts.city_state_postal'),
+ '$contact.last_name_label' => ctrans('texts.last_name'),
+ '$company.country_2_label' => ctrans('texts.country'),
+ '$company.id_number_label' => ctrans('texts.id_number'),
+ '$product.product1_label' => ctrans('texts.product1'),
+ '$product.product2_label' => ctrans('texts.product2'),
+ '$product.product3_label' => ctrans('texts.product3'),
+ '$product.product4_label' => ctrans('texts.product4'),
+ '$statement_amount_label' => ctrans('texts.amount'),
+ '$product.discount_label' => ctrans('texts.discount'),
+ '$assigned_to_user_label' => ctrans('texts.assigned_to'),
+ '$entity_issued_to_label' => ctrans('texts.purchase_order_issued_to'),
+ '$product.quantity_label' => ctrans('texts.quantity'),
+ '$total_tax_labels_label' => ctrans('texts.total_taxes'),
+ '$total_tax_label' => ctrans('texts.total_taxes'),
+ '$partial_due_date_label' => ctrans('texts.partial_due_date'),
+ '$company.address2_label' => ctrans('texts.address2'),
+ '$company.address1_label' => ctrans('texts.address1'),
+ '$vendor.id_number_label' => ctrans('texts.id_number'),
+ '$contact.custom1_label' => ctrans('texts.custom1'),
+ '$contact.custom2_label' => ctrans('texts.custom2'),
+ '$contact.custom3_label' => ctrans('texts.custom3'),
+ '$contact.custom4_label' => ctrans('texts.custom4'),
+ '$secondary_color_label' => ctrans('texts.secondary_color'),
+ '$company.custom1_label' => ctrans('texts.custom1'),
+ '$company.custom2_label' => ctrans('texts.custom2'),
+ '$company.custom3_label' => ctrans('texts.custom3'),
+ '$company.custom4_label' => ctrans('texts.custom4'),
+ '$balance_due_raw_label' => ctrans('texts.balance_due'),
+ '$entity.datetime_label' => ctrans('texts.datetime_format_id'),
+ '$vendor.address1_label' => ctrans('texts.address1'),
+ '$vendor.address2_label' => ctrans('texts.address2'),
+ '$line_tax_label' => ctrans('texts.line_taxes'),
+ '$line_tax_labels_label' => ctrans('texts.line_taxes'),
+ '$company.address_label' => ctrans('texts.address'),
+ '$user.first_name_label' => ctrans('texts.first_name'),
+ '$created_by_user_label' => ctrans('texts.created_by', ['name' => 'Manuel']),
+ '$vendor.currency_label' => ctrans('texts.currency'),
+ '$company.country_label' => ctrans('texts.country'),
+ '$tech_hero_image_label' => ctrans('texts.logo'),
+ '$company.website_label' => ctrans('texts.website'),
+ '$gross_subtotal_label' => ctrans('texts.subtotal'),
+ '$emailSignature_label' => ctrans('texts.email_signature'),
+ '$vendor_address_label' => ctrans('texts.address'),
+ '$vendor.address_label' => ctrans('texts.address'),
+ '$vendor.country_label' => ctrans('texts.country'),
+ '$vendor.custom3_label' => ctrans('texts.custom3'),
+ '$vendor.custom1_label' => ctrans('texts.custom1'),
+ '$vendor.custom4_label' => ctrans('texts.custom4'),
+ '$user.last_name_label' => ctrans('texts.last_name'),
+ '$vendor.custom2_label' => ctrans('texts.custom2'),
+ '$vendor.website_label' => ctrans('texts.website'),
+ '$dir_text_align_label' => '',
+ '$entity_footer_label' => ctrans('texts.footer'),
+ '$entity_images_label' => ctrans('texts.logo'),
+ '$contact.email_label' => ctrans('texts.email'),
+ '$primary_color_label' => ctrans('texts.primary_color'),
+ '$contact.phone_label' => ctrans('texts.phone'),
+ '$vendor.number_label' => ctrans('texts.number'),
+ '$company.phone_label' => ctrans('texts.phone'),
+ '$global_margin_label' => '',
+ '$company.state_label' => ctrans('texts.state'),
+ '$entity_number_label' => ctrans('texts.purchase_order_number'),
+ '$company.email_label' => ctrans('texts.email'),
+ '$product.date_label' => ctrans('texts.date'),
+ '$vendor.email_label' => ctrans('texts.email'),
+ '$entity.terms_label' => ctrans('texts.terms'),
+ '$product.item_label' => ctrans('texts.item'),
+ '$public_notes_label' => ctrans('texts.public_notes'),
+ '$paid_to_date_label' => ctrans('texts.paid_to_date'),
+ '$net_subtotal_label' => ctrans('texts.net_subtotal'),
+ '$payment.date_label' => ctrans('texts.date'),
+ '$vendor.phone_label' => ctrans('texts.phone'),
+ '$contact.name_label' => ctrans('texts.name'),
+ '$number_short_label' => ctrans('texts.purchase_order_number_short'),
+ '$company.name_label' => ctrans('texts.name'),
+ '$company.city_label' => ctrans('texts.city'),
+ '$vendor.state_label' => ctrans('texts.state'),
+ '$company.logo_label' => ctrans('texts.logo'),
+ '$company_logo_label' => ctrans('texts.logo'),
+ '$description_label' => ctrans('texts.description'),
+ '$product.tax_label' => ctrans('texts.tax'),
+ '$view_button_label' => ctrans('texts.link'),
+ '$status_logo_label' => ctrans('texts.logo'),
+ '$partial_due_label' => ctrans('texts.partial_due'),
+ '$balance_due_label' => ctrans('texts.balance_due'),
+ '$outstanding_label' => ctrans('texts.outstanding'),
+ '$payment_due_label' => ctrans('texts.payment_due'),
+ '$postal_city_label' => ctrans('texts.postal_city'),
+ '$vendor_name_label' => ctrans('texts.vendor_name'),
+ '$vendor.name_label' => ctrans('texts.name'),
+ '$vendor.city_label' => ctrans('texts.city'),
+ '$page_layout_label' => ctrans('texts.page_layout'),
+ '$viewButton_label' => ctrans('texts.view'),
+ '$amount_due_label' => ctrans('texts.amount_due'),
+ '$amount_raw_label' => ctrans('texts.amount'),
+ '$vat_number_label' => ctrans('texts.vat_number'),
+ '$portal_url_label' => ctrans('texts.link'),
+ '$po_number_label' => ctrans('texts.po_number'),
+ '$statement_label' => ctrans('texts.statement'),
+ '$view_link_label' => ctrans('texts.link'),
+ '$signature_label' => ctrans('texts.signature'),
+ '$font_size_label' => ctrans('texts.font_size'),
+ '$page_size_label' => ctrans('texts.page_size'),
+ '$country_2_label' => ctrans('texts.country'),
+ '$firstName_label' => ctrans('texts.name'),
+ '$id_number_label' => ctrans('texts.id_number'),
+ '$user.name_label' => ctrans('texts.name'),
+ '$font_name_label' => ctrans('texts.name'),
+ '$auto_bill_label' => ctrans('texts.auto_bill'),
+ '$poNumber_label' => ctrans('texts.po_number'),
+ '$payments_label' => ctrans('texts.payments'),
+ '$viewLink_label' => ctrans('texts.link'),
+ '$subtotal_label' => ctrans('texts.subtotal'),
+ '$company1_label' => ctrans('texts.company1'),
+ '$company2_label' => ctrans('texts.company2'),
+ '$company3_label' => ctrans('texts.company3'),
+ '$company4_label' => ctrans('texts.company4'),
+ '$due_date_label' => ctrans('texts.due_date'),
+ '$discount_label' => ctrans('texts.discount'),
+ '$address1_label' => ctrans('texts.address1'),
+ '$address2_label' => ctrans('texts.address2'),
+ '$autoBill_label' => ctrans('texts.auto_bill'),
+ '$view_url_label' => ctrans('texts.url'),
+ '$font_url_label' => ctrans('texts.url'),
+ '$details_label' => ctrans('texts.details'),
+ '$balance_label' => ctrans('texts.balance'),
+ '$partial_label' => ctrans('texts.partial'),
+ '$custom1_label' => ctrans('texts.custom1'),
+ '$custom2_label' => ctrans('texts.custom2'),
+ '$custom3_label' => ctrans('texts.custom3'),
+ '$custom4_label' => ctrans('texts.custom4'),
+ '$dueDate_label' => ctrans('texts.due_date'),
+ '$country_label' => ctrans('texts.country'),
+ '$vendor3_label' => ctrans('texts.vendor3'),
+ '$contact_label' => ctrans('texts.contact'),
+ '$account_label' => ctrans('texts.company'),
+ '$vendor1_label' => ctrans('texts.vendor1'),
+ '$vendor4_label' => ctrans('texts.vendor4'),
+ '$vendor2_label' => ctrans('texts.vendor2'),
+ '$website_label' => ctrans('texts.website'),
+ '$app_url_label' => ctrans('texts.url'),
+ '$footer_label' => ctrans('texts.footer'),
+ '$entity_label' => ctrans('texts.purchase_order'),
+ '$thanks_label' => ctrans('texts.thanks'),
+ '$amount_label' => ctrans('texts.amount'),
+ '$method_label' => ctrans('texts.method'),
+ '$vendor_label' => ctrans('texts.vendor'),
+ '$number_label' => ctrans('texts.number'),
+ '$email_label' => ctrans('texts.email'),
+ '$terms_label' => ctrans('texts.terms'),
+ '$notes_label' => ctrans('texts.notes'),
+ '$tax_rate1_label' => ctrans('texts.tax_rate1'),
+ '$tax_rate2_label' => ctrans('texts.tax_rate2'),
+ '$tax_rate3_label' => ctrans('texts.tax_rate3'),
+ '$total_label' => ctrans('texts.total'),
+ '$taxes_label' => ctrans('texts.taxes'),
+ '$phone_label' => ctrans('texts.phone'),
+ '$from_label' => ctrans('texts.from'),
+ '$item_label' => ctrans('texts.item'),
+ '$date_label' => ctrans('texts.date'),
+ '$tax_label' => ctrans('texts.tax'),
+ '$dir_label' => '',
+ '$to_label' => ctrans('texts.to'),
+ ];
+ }
}
diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php
index c10c96b15766..eccdb0d4dfc3 100644
--- a/app/Services/PdfMaker/Design.php
+++ b/app/Services/PdfMaker/Design.php
@@ -679,7 +679,12 @@ class Design extends BaseDesign
'$task.rate' => '$task.cost',
];
- foreach ($this->context['pdf_variables']["{$type}_columns"] as $column) {
+ $table_type = "{$type}_columns";
+
+ if($type == 'product' && $this->entity instanceof Quote && !$this->settings_object->getSetting('sync_invoice_quote_columns'))
+ $table_type = "product_quote_columns";
+
+ foreach ($this->context['pdf_variables'][$table_type] as $column) {
if (array_key_exists($column, $aliases)) {
$elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($aliases[$column], 1) . '-th', 'hidden' => $this->settings_object->getSetting('hide_empty_columns_on_pdf')]];
} elseif ($column == '$product.discount' && !$this->company->enable_product_discount) {
@@ -748,6 +753,14 @@ class Design extends BaseDesign
return $elements;
}
+ $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
+ $table_type = "{$_type}_columns";
+
+ if ($_type == 'product' && $this->entity instanceof Quote && !$this->settings_object->getSetting('sync_invoice_quote_columns')) {
+ $table_type = "product_quote_columns";
+ }
+
+
foreach ($items as $row) {
$element = ['element' => 'tr', 'elements' => []];
@@ -775,9 +788,8 @@ class Design extends BaseDesign
}
}
} else {
- $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
- foreach ($this->context['pdf_variables']["{$_type}_columns"] as $key => $cell) {
+ foreach ($this->context['pdf_variables'][$table_type] as $key => $cell) {
// We want to keep aliases like these:
// $task.cost => $task.rate
// $task.quantity => $task.hours
diff --git a/app/Services/Quote/SendEmail.php b/app/Services/Quote/SendEmail.php
index 1d40bc51835f..3fb32229612e 100644
--- a/app/Services/Quote/SendEmail.php
+++ b/app/Services/Quote/SendEmail.php
@@ -13,8 +13,7 @@ namespace App\Services\Quote;
use App\Jobs\Entity\EmailEntity;
use App\Models\ClientContact;
-use App\Services\Email\MailEntity;
-use App\Services\Email\MailObject;
+
class SendEmail
{
@@ -46,11 +45,10 @@ class SendEmail
$this->reminder_template = $this->quote->calculateTemplate('quote');
}
- $mo = new MailObject();
$this->quote->service()->markSent()->save();
- $this->quote->invitations->each(function ($invitation) use ($mo) {
+ $this->quote->invitations->each(function ($invitation) {
if (! $invitation->contact->trashed() && $invitation->contact->email) {
EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template);
diff --git a/app/Services/Recurring/IncreasePrice.php b/app/Services/Recurring/IncreasePrice.php
new file mode 100644
index 000000000000..b8486a206710
--- /dev/null
+++ b/app/Services/Recurring/IncreasePrice.php
@@ -0,0 +1,43 @@
+recurring_invoice->line_items;
+ foreach ($line_items as $key => $line_item) {
+
+ $line_items[$key]->cost = $line_item->cost * (1 + round(($this->percentage / 100), 2));
+
+ }
+
+ $this->recurring_invoice->line_items = $line_items;
+ $this->recurring_invoice->calc()->getInvoice()->save();
+
+
+
+ }
+
+
+}
diff --git a/app/Services/Recurring/RecurringService.php b/app/Services/Recurring/RecurringService.php
index 7a056a5c1d32..5c20a19bc5ed 100644
--- a/app/Services/Recurring/RecurringService.php
+++ b/app/Services/Recurring/RecurringService.php
@@ -11,18 +11,22 @@
namespace App\Services\Recurring;
-use App\Jobs\RecurringInvoice\SendRecurring;
use App\Jobs\Util\UnlinkFile;
-use App\Models\RecurringInvoice;
use Illuminate\Support\Carbon;
+use App\Models\RecurringExpense;
+use App\Models\RecurringInvoice;
+use App\Services\Recurring\ApplyNumber;
+use App\Services\Recurring\UpdatePrice;
+use App\Services\Recurring\GetInvoicePdf;
+use App\Services\Recurring\IncreasePrice;
+use App\Jobs\RecurringInvoice\SendRecurring;
+use App\Services\Recurring\CreateRecurringInvitations;
class RecurringService
{
- protected $recurring_entity;
- public function __construct($recurring_entity)
+ public function __construct(public RecurringInvoice | RecurringExpense $recurring_entity)
{
- $this->recurring_entity = $recurring_entity;
}
//set schedules - update next_send_dates
@@ -135,6 +139,21 @@ class RecurringService
{
return $this;
}
+
+ public function increasePrice(float $percentage)
+ {
+ (new IncreasePrice($this->recurring_entity, $percentage))->run();
+
+ return $this;
+
+ }
+
+ public function updatePrice()
+ {
+ (new UpdatePrice($this->recurring_entity))->run();
+
+ return $this;
+ }
public function save()
{
diff --git a/app/Services/Recurring/UpdatePrice.php b/app/Services/Recurring/UpdatePrice.php
new file mode 100644
index 000000000000..1e196c68bc90
--- /dev/null
+++ b/app/Services/Recurring/UpdatePrice.php
@@ -0,0 +1,50 @@
+recurring_invoice->line_items;
+
+ foreach($line_items as $key => $line_item)
+ {
+
+ $product = Product::where('company_id', $this->recurring_invoice->company_id)
+ ->where('product_key', $line_item->product_key)
+ ->where('is_deleted', 0)
+ ->first();
+
+ if($product){
+
+ $line_items[$key]->cost = $product->cost;
+ }
+
+ }
+
+ $this->recurring_invoice->line_items = $line_items;
+ $this->recurring_invoice->calc()->getInvoice()->save();
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/Transformers/CompanyUserTransformer.php b/app/Transformers/CompanyUserTransformer.php
index f507ec603a6c..6ae6f81474f4 100644
--- a/app/Transformers/CompanyUserTransformer.php
+++ b/app/Transformers/CompanyUserTransformer.php
@@ -80,7 +80,11 @@ class CompanyUserTransformer extends EntityTransformer
public function includeToken(CompanyUser $company_user)
{
- $token = $company_user->tokens()->where('company_id', $company_user->company_id)->where('user_id', $company_user->user_id)->first();
+ $token = $company_user->tokens()
+ ->where('company_id', $company_user->company_id)
+ ->where('user_id', $company_user->user_id)
+ ->where('is_system', 1)
+ ->first();
$transformer = new CompanyTokenTransformer($this->serializer);
diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php
index 8aa488d454a8..3872ef95666e 100644
--- a/app/Utils/HtmlEngine.php
+++ b/app/Utils/HtmlEngine.php
@@ -683,6 +683,8 @@ class HtmlEngine
$data['labels'][$key.'_label'] = $value['label'];
}
+ // nlog($data);
+
return $data;
}
@@ -762,9 +764,6 @@ class HtmlEngine
if ($country) {
return $country->iso_3166_2;
}
- // if ($country) {
- // return ctrans('texts.country_' . $country->iso_3166_2);
- // }
return ' ';
}
diff --git a/app/Utils/VendorHtmlEngine.php b/app/Utils/VendorHtmlEngine.php
index aab9b9fb3302..4eac57ac59bc 100644
--- a/app/Utils/VendorHtmlEngine.php
+++ b/app/Utils/VendorHtmlEngine.php
@@ -511,7 +511,7 @@ class VendorHtmlEngine
$data['values'][$key] = $value['value'];
$data['labels'][$key.'_label'] = $value['label'];
}
-
+nlog($data);
return $data;
}
diff --git a/composer.lock b/composer.lock
index 6483f46fdb0d..6cf928a7b942 100644
--- a/composer.lock
+++ b/composer.lock
@@ -2171,16 +2171,16 @@
},
{
"name": "google/apiclient-services",
- "version": "v0.289.0",
+ "version": "v0.290.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
- "reference": "937f83a927db2d09db7eebb69ce2ac4114559bd7"
+ "reference": "df7e6cbab08f60509b3f360d8286c194ad2930e2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/937f83a927db2d09db7eebb69ce2ac4114559bd7",
- "reference": "937f83a927db2d09db7eebb69ce2ac4114559bd7",
+ "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/df7e6cbab08f60509b3f360d8286c194ad2930e2",
+ "reference": "df7e6cbab08f60509b3f360d8286c194ad2930e2",
"shasum": ""
},
"require": {
@@ -2209,9 +2209,9 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
- "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.289.0"
+ "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.290.0"
},
- "time": "2023-02-26T01:10:11+00:00"
+ "time": "2023-03-01T17:20:18+00:00"
},
{
"name": "google/auth",
@@ -14019,16 +14019,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
- "version": "v3.14.4",
+ "version": "v3.15.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
- "reference": "1b3d9dba63d93b8a202c31e824748218781eae6b"
+ "reference": "7306744c63e9cc1337894252b4eec4920c38b053"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1b3d9dba63d93b8a202c31e824748218781eae6b",
- "reference": "1b3d9dba63d93b8a202c31e824748218781eae6b",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7306744c63e9cc1337894252b4eec4920c38b053",
+ "reference": "7306744c63e9cc1337894252b4eec4920c38b053",
"shasum": ""
},
"require": {
@@ -14095,9 +14095,15 @@
}
],
"description": "A tool to automatically fix PHP code style",
+ "keywords": [
+ "Static code analysis",
+ "fixer",
+ "standards",
+ "static analysis"
+ ],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
- "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.4"
+ "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.15.0"
},
"funding": [
{
@@ -14105,7 +14111,7 @@
"type": "github"
}
],
- "time": "2023-02-09T21:49:13+00:00"
+ "time": "2023-03-12T22:44:55+00:00"
},
{
"name": "hamcrest/hamcrest-php",
diff --git a/config/liap.php b/config/liap.php
index b7f91958ab8e..f140554cf65f 100644
--- a/config/liap.php
+++ b/config/liap.php
@@ -1,5 +1,10 @@
env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
- 'app_version' => '5.5.89',
- 'app_tag' => '5.5.89',
+ 'app_version' => '5.5.90',
+ 'app_tag' => '5.5.90',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),
diff --git a/config/purchase.php b/config/purchase.php
deleted file mode 100644
index 6501dc836aaa..000000000000
--- a/config/purchase.php
+++ /dev/null
@@ -1,74 +0,0 @@
- [],
-
- 'google_play_package_name' => env('GOOGLE_PLAY_PACKAGE_NAME', 'com.invoiceninja.app'),
-
- 'appstore_password' => env('APPSTORE_PASSWORD', ''),
-
- 'eventListeners' => [
- /**
- * --------------------------------------------------------
- * Google Play Events
- * --------------------------------------------------------
- */
- SubscriptionPurchased::class => [],
- SubscriptionRenewed::class => [PlayStoreRenewSubscription::class],
- SubscriptionInGracePeriod::class => [],
- SubscriptionExpired::class => [],
- SubscriptionCanceled::class => [],
- SubscriptionPaused::class => [],
- SubscriptionRestarted::class => [],
- SubscriptionDeferred::class => [],
- SubscriptionRevoked::class => [],
- SubscriptionOnHold::class => [],
- SubscriptionRecovered::class => [],
- SubscriptionPauseScheduleChanged::class => [],
- SubscriptionPriceChangeConfirmed::class => [],
-
- /**
- * --------------------------------------------------------
- * Appstore Events
- * --------------------------------------------------------
- */
- Cancel::class => [],
- DidChangeRenewalPref::class => [],
- DidChangeRenewalStatus::class => [],
- DidFailToRenew::class => [],
- DidRecover::class => [],
- DidRenew::class => [AppStoreRenewSubscription::class],
- InitialBuy::class => [],
- InteractiveRenewal::class => [],
- PriceIncreaseConsent::class => [],
- Refund::class => [],
- Revoke::class => [],
- ],
-];
\ No newline at end of file
diff --git a/resources/views/email/template/client.blade.php b/resources/views/email/template/client.blade.php
index 544437512b3d..d99d69d3dd76 100644
--- a/resources/views/email/template/client.blade.php
+++ b/resources/views/email/template/client.blade.php
@@ -118,6 +118,9 @@
background-color: {{ $primary_color }};
}
+ .logo {
+
+ }