diff --git a/VERSION.txt b/VERSION.txt
index 8fa0157a0207..8a55abf0b000 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-5.5.40
\ No newline at end of file
+5.5.41
\ No newline at end of file
diff --git a/app/Console/Commands/SendRemindersCron.php b/app/Console/Commands/SendRemindersCron.php
index f8f69a30dd60..ad0b99387a65 100644
--- a/app/Console/Commands/SendRemindersCron.php
+++ b/app/Console/Commands/SendRemindersCron.php
@@ -175,7 +175,7 @@ class SendRemindersCron extends Command
/**Refresh Invoice values*/
$invoice->calc()->getInvoice()->save();
$invoice->fresh();
- $invoice->service()->deletePdf();
+ $invoice->service()->deletePdf()->save();
/* Refresh the client here to ensure the balance is fresh */
$client = $invoice->client;
diff --git a/app/Filters/BankTransactionFilters.php b/app/Filters/BankTransactionFilters.php
index f76cc81cfdb7..e7cff05feee9 100644
--- a/app/Filters/BankTransactionFilters.php
+++ b/app/Filters/BankTransactionFilters.php
@@ -55,6 +55,55 @@ class BankTransactionFilters extends QueryFilters
}
+
+/**
+ * Filter based on client status.
+ *
+ * Statuses we need to handle
+ * - all
+ * - unmatched
+ * - matched
+ * - converted
+ * - deposits
+ * - withdrawals
+ *
+ * @return Builder
+ */
+ public function client_status(string $value = '') :Builder
+ {
+ if (strlen($value) == 0) {
+ return $this->builder;
+ }
+
+ $status_parameters = explode(',', $value);
+
+ if (in_array('all', $status_parameters)) {
+ return $this->builder;
+ }
+
+ if (in_array('unmatched', $status_parameters)) {
+ $this->builder->where('status_id', BankTransaction::STATUS_UNMATCHED);
+ }
+
+ if (in_array('matched', $status_parameters)) {
+ $this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
+ }
+
+ if (in_array('converted', $status_parameters)) {
+ $this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
+ }
+
+ if (in_array('deposits', $status_parameters)) {
+ $this->builder->where('base_type', 'CREDIT');
+ }
+
+ if (in_array('withdrawals', $status_parameters)) {
+ $this->builder->where('base_type', 'DEBIT');
+ }
+
+ return $this->builder;
+ }
+
/**
* Filters the list based on the status
* archived, active, deleted.
diff --git a/app/Filters/ExpenseFilters.php b/app/Filters/ExpenseFilters.php
index 174acff790af..d7e10ac0f4f1 100644
--- a/app/Filters/ExpenseFilters.php
+++ b/app/Filters/ExpenseFilters.php
@@ -44,6 +44,55 @@ class ExpenseFilters extends QueryFilters
});
}
+ /**
+ * Filter based on client status.
+ *
+ * Statuses we need to handle
+ * - all
+ * - logged
+ * - pending
+ * - invoiced
+ * - paid
+ * - unpaid
+ *
+ * @return Builder
+ */
+ public function client_status(string $value = '') :Builder
+ {
+ if (strlen($value) == 0) {
+ return $this->builder;
+ }
+
+ $status_parameters = explode(',', $value);
+
+ if (in_array('all', $status_parameters)) {
+ return $this->builder;
+ }
+
+ if (in_array('logged', $status_parameters)) {
+ $this->builder->where('amount', '>', 0);
+ }
+
+ if (in_array('pending', $status_parameters)) {
+ $this->builder->whereNull('invoice_id')->whereNotNull('payment_date');
+ }
+
+ if (in_array('invoiced', $status_parameters)) {
+ $this->builder->whereNotNull('invoice_id');
+ }
+
+ if (in_array('paid', $status_parameters)) {
+ $this->builder->whereNotNull('payment_date');
+ }
+
+ if (in_array('unpaid', $status_parameters)) {
+ $this->builder->whereNull('payment_date');
+ }
+
+ return $this->builder;
+ }
+
+
/**
* Filters the list based on the status
* archived, active, deleted.
diff --git a/app/Filters/PurchaseOrderFilters.php b/app/Filters/PurchaseOrderFilters.php
index 3aa91958ef36..2d9f957d75f6 100644
--- a/app/Filters/PurchaseOrderFilters.php
+++ b/app/Filters/PurchaseOrderFilters.php
@@ -17,19 +17,20 @@ use Illuminate\Database\Eloquent\Builder;
class PurchaseOrderFilters extends QueryFilters
{
+
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
- * - paid
- * - unpaid
- * - overdue
- * - reversed
+ * - draft
+ * - sent
+ * - accepted
+ * - cancelled
*
* @return Builder
*/
- public function credit_status(string $value = '') :Builder
+ public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
@@ -45,16 +46,17 @@ class PurchaseOrderFilters extends QueryFilters
$this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT);
}
- if (in_array('partial', $status_parameters)) {
- $this->builder->where('status_id', PurchaseOrder::STATUS_PARTIAL);
+ if (in_array('sent', $status_parameters)) {
+ $this->builder->where('status_id', PurchaseOrder::STATUS_SENT);
}
- if (in_array('applied', $status_parameters)) {
- $this->builder->where('status_id', PurchaseOrder::STATUS_APPLIED);
+ if (in_array('accepted', $status_parameters)) {
+ $this->builder->where('status_id', PurchaseOrder::STATUS_ACCEPTED);
}
- //->where('due_date', '>', Carbon::now())
- //->orWhere('partial_due_date', '>', Carbon::now());
+ if (in_array('cancelled', $status_parameters)) {
+ $this->builder->where('status_id', PurchaseOrder::STATUS_CANCELLED);
+ }
return $this->builder;
}
diff --git a/app/Filters/QuoteFilters.php b/app/Filters/QuoteFilters.php
index f619559b621c..9942ee909c29 100644
--- a/app/Filters/QuoteFilters.php
+++ b/app/Filters/QuoteFilters.php
@@ -11,6 +11,7 @@
namespace App\Filters;
+use App\Models\Quote;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
@@ -41,6 +42,51 @@ class QuoteFilters extends QueryFilters
});
}
+ /**
+ * Filter based on client status.
+ *
+ * Statuses we need to handle
+ * - all
+ * - active
+ * - paused
+ * - completed
+ *
+ * @param string client_status The invoice status as seen by the client
+ * @return Builder
+ */
+ public function client_status(string $value = '') :Builder
+ {
+ if (strlen($value) == 0) {
+ return $this->builder;
+ }
+
+ $status_parameters = explode(',', $value);
+
+ if (in_array('all', $status_parameters)) {
+ return $this->builder;
+ }
+
+ if (in_array('draft', $status_parameters)) {
+ $this->builder->where('status_id', Quote::STATUS_DRAFT);
+ }
+
+ if (in_array('sent', $status_parameters)) {
+ $this->builder->where('status_id', Quote::STATUS_SENT);
+ }
+
+ if (in_array('approved', $status_parameters)) {
+ $this->builder->where('status_id', Quote::STATUS_APPROVED);
+ }
+
+ if (in_array('expired', $status_parameters)) {
+ $this->builder->where('status_id', Quote::STATUS_SENT)
+ ->where('due_date', '<=', now()->toDateString());
+ }
+
+ return $this->builder;
+ }
+
+
/**
* Filters the list based on the status
* archived, active, deleted.
diff --git a/app/Filters/RecurringInvoiceFilters.php b/app/Filters/RecurringInvoiceFilters.php
index 39f0bcaa0de7..541f24c03617 100644
--- a/app/Filters/RecurringInvoiceFilters.php
+++ b/app/Filters/RecurringInvoiceFilters.php
@@ -11,6 +11,7 @@
namespace App\Filters;
+use App\Models\RecurringInvoice;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
@@ -40,6 +41,46 @@ class RecurringInvoiceFilters extends QueryFilters
});
}
+ /**
+ * Filter based on client status.
+ *
+ * Statuses we need to handle
+ * - all
+ * - active
+ * - paused
+ * - completed
+ *
+ * @param string client_status The invoice status as seen by the client
+ * @return Builder
+ */
+ public function client_status(string $value = '') :Builder
+ {
+ if (strlen($value) == 0) {
+ return $this->builder;
+ }
+
+ $status_parameters = explode(',', $value);
+
+ if (in_array('all', $status_parameters)) {
+ return $this->builder;
+ }
+
+ if (in_array('active', $status_parameters)) {
+ $this->builder->where('status_id', RecurringInvoice::STATUS_ACTIVE);
+ }
+
+ if (in_array('paused', $status_parameters)) {
+ $this->builder->where('status_id', RecurringInvoice::STATUS_PAUSED);
+ }
+
+ if (in_array('completed', $status_parameters)) {
+ $this->builder->where('status_id', RecurringInvoice::STATUS_COMPLETED);
+ }
+
+ return $this->builder;
+ }
+
+
/**
* Filters the list based on the status
* archived, active, deleted.
diff --git a/app/Filters/TaskFilters.php b/app/Filters/TaskFilters.php
index d0f7f2419c05..f8278f506043 100644
--- a/app/Filters/TaskFilters.php
+++ b/app/Filters/TaskFilters.php
@@ -41,6 +41,37 @@ class TaskFilters extends QueryFilters
});
}
+
+ /**
+ * Filter based on client status.
+ *
+ * Statuses we need to handle
+ * - all
+ * - invoiced
+ *
+ * @param string client_status The invoice status as seen by the client
+ * @return Builder
+ */
+ public function client_status(string $value = '') :Builder
+ {
+ if (strlen($value) == 0) {
+ return $this->builder;
+ }
+
+ $status_parameters = explode(',', $value);
+
+ if (in_array('all', $status_parameters)) {
+ return $this->builder;
+ }
+
+ if (in_array('invoiced', $status_parameters)) {
+ $this->builder->whereNotNull('invoice_id');
+ }
+
+ return $this->builder;
+ }
+
+
/**
* Filters the list based on the status
* archived, active, deleted.
diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php
index d62d14c19037..56400e7e1285 100644
--- a/app/Http/Controllers/BaseController.php
+++ b/app/Http/Controllers/BaseController.php
@@ -1000,42 +1000,6 @@ class BaseController extends Controller
return redirect('/setup');
}
- public function reactCatch()
- {
-
- if ((bool) $this->checkAppSetup() !== false && $account = Account::first()) {
- if (config('ninja.require_https') && ! request()->isSecure()) {
- return redirect()->secure(request()->getRequestUri());
- }
-
- $data = [];
-
- //pass report errors bool to front end
- $data['report_errors'] = Ninja::isSelfHost() ? $account->report_errors : true;
-
- //pass referral code to front end
- $data['rc'] = request()->has('rc') ? request()->input('rc') : '';
- $data['build'] = request()->has('build') ? request()->input('build') : '';
- $data['login'] = request()->has('login') ? request()->input('login') : 'false';
- $data['signup'] = request()->has('signup') ? request()->input('signup') : 'false';
-
- $data['user_agent'] = request()->server('HTTP_USER_AGENT');
-
- $data['path'] = $this->setBuild();
-
- $this->buildCache();
-
- if (Ninja::isSelfHost() && $account->set_react_as_default_ap) {
- return view('react.index', $data);
- } else {
- abort('page not found', 404);
- }
- }
-
- return redirect('/setup');
- }
-
-
private function setBuild()
{
$build = '';
diff --git a/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php b/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php
index 76dc1e349c87..280565926737 100644
--- a/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php
+++ b/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php
@@ -31,12 +31,12 @@ class MatchBankTransactionRequest extends Request
$rules = [
'transactions' => 'bail|array',
- 'transactions.*.id' => 'bail|required',
'transactions.*.invoice_ids' => 'nullable|string|sometimes',
];
$rules['transactions.*.ninja_category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['transactions.*.vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
+ $rules['transactions.*.id'] = 'bail|required|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $rules;
diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php
index 37cf05e5400e..94797598d43c 100644
--- a/app/Http/Requests/Company/UpdateCompanyRequest.php
+++ b/app/Http/Requests/Company/UpdateCompanyRequest.php
@@ -22,6 +22,14 @@ class UpdateCompanyRequest extends Request
{
use MakesHash;
+ private array $protected_input = [
+ 'client_portal_privacy_policy',
+ 'client_portal_terms',
+ 'portal_custom_footer',
+ 'portal_custom_css',
+ 'portal_custom_head'
+ ];
+
/**
* Determine if the user is authorized to make this request.
*
@@ -32,6 +40,8 @@ class UpdateCompanyRequest extends Request
return auth()->user()->can('edit', $this->company);
}
+
+
public function rules()
{
$input = $this->all();
@@ -90,6 +100,14 @@ class UpdateCompanyRequest extends Request
{
$account = $this->company->account;
+ if(Ninja::isHosted())
+ {
+ foreach($this->protected_input as $protected_var)
+ {
+ $settings[$protected_var] = str_replace("script", "", $settings[$protected_var]);
+ }
+ }
+
if (! $account->isFreeHostedClient()) {
return $settings;
}
diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php
index 17e3a61a960d..d99c8fadb644 100644
--- a/app/Jobs/Bank/MatchBankTransactions.php
+++ b/app/Jobs/Bank/MatchBankTransactions.php
@@ -160,6 +160,9 @@ class MatchBankTransactions implements ShouldQueue
{
$this->bt = BankTransaction::find($input['id']);
+ if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
+ return $this;
+
$_invoices = Invoice::withTrashed()->find($this->getInvoices($input['invoice_ids']));
$amount = $this->bt->amount;
@@ -180,6 +183,10 @@ class MatchBankTransactions implements ShouldQueue
//if there is a category id, pull it from Yodlee and insert - or just reuse!!
$this->bt = BankTransaction::find($input['id']);
+ if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
+ return $this;
+
+
$expense = ExpenseFactory::create($this->bt->company_id, $this->bt->user_id);
$expense->category_id = $this->resolveCategory($input);
$expense->amount = $this->bt->amount;
diff --git a/app/Listeners/Invoice/InvoiceEmailFailedActivity.php b/app/Listeners/Invoice/InvoiceEmailFailedActivity.php
index 122bdc972379..b46493528fb8 100644
--- a/app/Listeners/Invoice/InvoiceEmailFailedActivity.php
+++ b/app/Listeners/Invoice/InvoiceEmailFailedActivity.php
@@ -21,8 +21,6 @@ class InvoiceEmailFailedActivity implements ShouldQueue
{
protected $activity_repo;
- public $delay = 5;
-
/**
* Create the event listener.
*
diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php
index f1eaac47ffb5..bf15b7aa2ca4 100644
--- a/app/Mail/Engine/PaymentEmailEngine.php
+++ b/app/Mail/Engine/PaymentEmailEngine.php
@@ -243,6 +243,14 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$invoices.due_date'] = ['value' => $this->formatInvoiceField('due_date'), 'label' => ctrans('texts.invoices')];
$data['$invoices.po_number'] = ['value' => $this->formatInvoiceField('po_number'), 'label' => ctrans('texts.invoices')];
+
+ if($this->payment->status_id == 4) {
+ $data['$status_logo'] = ['value' => '
' . ctrans('texts.paid') .'
', 'label' => ''];
+ }
+ else
+ $data['$status_logo'] = ['value' => '', 'label' => ''];
+
+
$arrKeysLength = array_map('strlen', array_keys($data));
array_multisort($arrKeysLength, SORT_DESC, $data);
diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreateCustomer.php b/app/PaymentDrivers/Authorize/AuthorizeCreateCustomer.php
index 84477c155554..a49b98713a2b 100644
--- a/app/PaymentDrivers/Authorize/AuthorizeCreateCustomer.php
+++ b/app/PaymentDrivers/Authorize/AuthorizeCreateCustomer.php
@@ -136,3 +136,36 @@ class AuthorizeCreateCustomer
// }
// }
}
+
+// $request = new net\authorize\api\contract\v1\GetCustomerProfileIdsRequest();
+// $request->setMerchantAuthentication($auth->merchant_authentication);
+// $controller = new net\authorize\api\controller\GetCustomerProfileIdsController($request);
+// $response = $controller->executeWithApiResponse($auth->mode());
+
+// // $customer_profile_id = end($response->getIds());
+
+// foreach($response->getIds() as $customer_profile_id)
+// {
+// $request = new net\authorize\api\contract\v1\GetCustomerProfileRequest();
+// $request->setMerchantAuthentication($auth->merchant_authentication);
+// $request->setCustomerProfileId($customer_profile_id);
+// $controller = new net\authorize\api\controller\GetCustomerProfileController($request);
+// $response = $controller->executeWithApiResponse($auth->mode());
+
+// $profileSelected = $response->getProfile();
+
+// if($profileSelected->getEmail() == 'katnandan@gmail.com')
+// {
+
+// $profileSelected;
+// break;
+
+// }
+
+
+// }
+
+
+
+
+
diff --git a/app/PaymentDrivers/Forte/CreditCard.php b/app/PaymentDrivers/Forte/CreditCard.php
index 336ef7b525b1..2b851a88b7fb 100644
--- a/app/PaymentDrivers/Forte/CreditCard.php
+++ b/app/PaymentDrivers/Forte/CreditCard.php
@@ -92,12 +92,14 @@ class CreditCard
$payment_hash = PaymentHash::where('hash', $request->input('payment_hash'))->firstOrFail();
$amount_with_fee = $payment_hash->data->total->amount_with_fee;
$invoice_totals = $payment_hash->data->total->invoice_totals;
- $fee_total = 0;
+ $fee_total = null;
$fees_and_limits = $this->forte->company_gateway->getFeesAndLimits(GatewayType::CREDIT_CARD);
if(property_exists($fees_and_limits, 'fee_percent') && $fees_and_limits->fee_percent > 0)
{
+ $fee_total = 0;
+
for ($i = ($invoice_totals * 100) ; $i < ($amount_with_fee * 100); $i++) {
$calculated_fee = ( 3 * $i) / 100;
$calculated_amount_with_fee = round(($i + $calculated_fee) / 100,2);
diff --git a/app/PaymentDrivers/GoCardless/InstantBankPay.php b/app/PaymentDrivers/GoCardless/InstantBankPay.php
index a5f8aafa7f21..3d5cf238272a 100644
--- a/app/PaymentDrivers/GoCardless/InstantBankPay.php
+++ b/app/PaymentDrivers/GoCardless/InstantBankPay.php
@@ -97,6 +97,8 @@ class InstantBankPay implements MethodInterface
$this->go_cardless->setPaymentHash(
$request->getPaymentHash()
);
+
+ $this->go_cardless->init();
try {
$billing_request = $this->go_cardless->gateway->billingRequests()->get(
diff --git a/app/PaymentDrivers/GoCardlessPaymentDriver.php b/app/PaymentDrivers/GoCardlessPaymentDriver.php
index a1db5c298a48..24822c2b289c 100644
--- a/app/PaymentDrivers/GoCardlessPaymentDriver.php
+++ b/app/PaymentDrivers/GoCardlessPaymentDriver.php
@@ -291,13 +291,13 @@ class GoCardlessPaymentDriver extends BaseDriver
return response()->json([], 200);
}
- $this->go_cardless->setPaymentHash($hash);
+ $this->setPaymentHash($hash);
- $billing_request = $this->go_cardless->gateway->billingRequests()->get(
+ $billing_request = $this->gateway->billingRequests()->get(
$event['links']['billing_request']
);
- $payment = $this->go_cardless->gateway->payments()->get(
+ $payment = $this->gateway->payments()->get(
$billing_request->payment_request->links->payment
);
@@ -305,12 +305,12 @@ class GoCardlessPaymentDriver extends BaseDriver
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($hash->invoices(), 'invoice_id')))->withTrashed()->get();
- $this->go_cardless->client = $invoices->first()->client;
+ $this->client = $invoices->first()->client;
$invoices->each(function ($invoice){
//if payments exist already, they just need to be confirmed.
- if($invoice->payments()->exists){
+ if($invoice->payments()->exists()){
$invoice->payments()->where('status_id', 1)->cursor()->each(function ($payment){
$payment->status_id = 4;
@@ -347,12 +347,12 @@ class GoCardlessPaymentDriver extends BaseDriver
$data = [
'payment_method' => $payment->links->mandate,
'payment_type' => PaymentType::INSTANT_BANK_PAY,
- 'amount' => $this->go_cardless->payment_hash->data->amount_with_fee,
+ 'amount' => $this->payment_hash->data->amount_with_fee,
'transaction_reference' => $payment->id,
'gateway_type_id' => GatewayType::INSTANT_BANK_PAY,
];
- $payment = $this->go_cardless->createPayment($data, Payment::STATUS_COMPLETED);
+ $payment = $this->createPayment($data, Payment::STATUS_COMPLETED);
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
@@ -361,8 +361,8 @@ class GoCardlessPaymentDriver extends BaseDriver
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_GOCARDLESS,
- $this->go_cardless->client,
- $this->go_cardless->client->company,
+ $this->client,
+ $this->client->company,
);
}
diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php
index 4d9451dcf83a..110567910cda 100644
--- a/app/Repositories/BaseRepository.php
+++ b/app/Repositories/BaseRepository.php
@@ -187,7 +187,7 @@ class BaseRepository
if(!$model->id){
$this->new_model = true;
- if(is_array($model->line_items))
+ if(is_array($model->line_items) && !($model instanceof RecurringInvoice))
{
$model->line_items = (collect($model->line_items))->map(function ($item) use($model,$client) {
diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php
index 03e5e686a2ae..b47884b63a93 100644
--- a/app/Services/PdfMaker/Design.php
+++ b/app/Services/PdfMaker/Design.php
@@ -73,7 +73,8 @@ class Design extends BaseDesign
const PLAIN = 'plain';
const PLAYFUL = 'playful';
const CUSTOM = 'custom';
-
+ const CALM = 'calm';
+
const DELIVERY_NOTE = 'delivery_note';
const STATEMENT = 'statement';
const PURCHASE_ORDER = 'purchase_order';
diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php
index fee637ec83e7..e512ee3cb73b 100644
--- a/app/Utils/Helpers.php
+++ b/app/Utils/Helpers.php
@@ -114,7 +114,7 @@ class Helpers
return '';
}
- // 04-10-2022 Return Early if no reserved keywords are present, this is a very expenseive process
+ // 04-10-2022 Return Early if no reserved keywords are present, this is a very expensive process
$string_hit = false;
foreach ( [':MONTH',':YEAR',':QUARTER',':WEEK'] as $string )
@@ -146,19 +146,19 @@ class Helpers
'%s %s %s',
Carbon::now()->subDays(7)->translatedFormat($entity->date_format()),
ctrans('texts.to'),
- Carbon::now()->translatedFormat($entity->date_format())
+ Carbon::now()->subDays(1)->translatedFormat($entity->date_format())
),
':WEEK_AHEAD' => \sprintf(
'%s %s %s',
- Carbon::now()->addDays(7)->translatedFormat($entity->date_format()),
+ Carbon::now()->addDays(6)->translatedFormat($entity->date_format()),
ctrans('texts.to'),
- Carbon::now()->addDays(14)->translatedFormat($entity->date_format())
+ Carbon::now()->addDays(13)->translatedFormat($entity->date_format())
),
':WEEK' => \sprintf(
'%s %s %s',
- Carbon::now()->translatedFormat($entity->date_format()),
+ Carbon::now()->subDays(7)->translatedFormat($entity->date_format()),
ctrans('texts.to'),
- Carbon::now()->addDays(7)->translatedFormat($entity->date_format())
+ Carbon::now()->addDays(13)->translatedFormat($entity->date_format())
),
],
'raw' => [
diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php
index a022d5e1642e..003d5fb1f79f 100644
--- a/app/Utils/HtmlEngine.php
+++ b/app/Utils/HtmlEngine.php
@@ -124,6 +124,7 @@ class HtmlEngine
$data['$line_tax_labels'] = ['value' => $this->lineTaxLabels(), 'label' => ctrans('texts.taxes')];
$data['$line_tax_values'] = ['value' => $this->lineTaxValues(), 'label' => ctrans('texts.taxes')];
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.date')];
+ $data['$status_logo'] = ['value' => '', 'label' => ''];
$data['$invoice.date'] = &$data['$date'];
$data['$invoiceDate'] = &$data['$date'];
@@ -167,6 +168,10 @@ class HtmlEngine
$data['$invoice.project'] = &$data['$project.name'];
}
+ if($this->entity->status_id == 4) {
+ $data['$status_logo'] = ['value' => ' ' . ctrans('texts.paid') .'
', 'label' => ''];
+ }
+
if($this->entity->vendor) {
$data['$invoice.vendor'] = ['value' => $this->entity->vendor->present()->name(), 'label' => ctrans('texts.vendor_name')];
}
diff --git a/config/ninja.php b/config/ninja.php
index a3bb71301c2d..edfc4c68b0f2 100644
--- a/config/ninja.php
+++ b/config/ninja.php
@@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
- 'app_version' => '5.5.40',
- 'app_tag' => '5.5.40',
+ 'app_version' => '5.5.41',
+ 'app_tag' => '5.5.41',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),
diff --git a/database/migrations/2022_11_16_093535_calmness_design.php b/database/migrations/2022_11_16_093535_calmness_design.php
new file mode 100644
index 000000000000..898d96549a87
--- /dev/null
+++ b/database/migrations/2022_11_16_093535_calmness_design.php
@@ -0,0 +1,53 @@
+name = 'Calm';
+ $design->is_custom = false;
+ $design->design = '';
+ $design->is_active = true;
+
+ $design->save();
+ } elseif (Design::count() !== 0) {
+ $design = new Design();
+
+ $design->name = 'Calm';
+ $design->is_custom = false;
+ $design->design = '';
+ $design->is_active = true;
+
+ $design->save();
+ }
+
+ \Illuminate\Support\Facades\Artisan::call('ninja:design-update');
+
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ //
+ }
+};
diff --git a/database/seeders/DesignSeeder.php b/database/seeders/DesignSeeder.php
index fb17cba8ff2e..f1a1fd0ede38 100644
--- a/database/seeders/DesignSeeder.php
+++ b/database/seeders/DesignSeeder.php
@@ -38,6 +38,7 @@ class DesignSeeder extends Seeder
['id' => 8, 'name' => 'Hipster', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 9, 'name' => 'Playful', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
['id' => 10, 'name' => 'Tech', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
+ ['id' => 11, 'name' => 'Calm', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true],
];
foreach ($designs as $design) {
diff --git a/lang/en/texts.php b/lang/en/texts.php
index fe2e75ac1590..3817b0d65129 100644
--- a/lang/en/texts.php
+++ b/lang/en/texts.php
@@ -4839,6 +4839,10 @@ $LANG = array(
'show_tasks_in_client_portal' => 'Show Tasks in Client Portal',
'notification_quote_expired_subject' => 'Quote :invoice has expired for :client',
'notification_quote_expired' => 'The following Quote :invoice for client :client and :amount has now expired.',
+ 'auto_sync' => 'Auto Sync',
+ 'refresh_accounts' => 'Refresh Accounts',
+ 'upgrade_to_connect_bank_account' => 'Upgrade to Enterprise to connect your bank account',
+ 'click_here_to_connect_bank_account' => 'Click here to connect your bank account',
);
return $LANG;
diff --git a/resources/views/email/template/admin.blade.php b/resources/views/email/template/admin.blade.php
index 82b9df9e55a5..7aecb654c82e 100644
--- a/resources/views/email/template/admin.blade.php
+++ b/resources/views/email/template/admin.blade.php
@@ -101,6 +101,36 @@
#content .center {
text-align: center;
}
+
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
diff --git a/resources/views/email/template/client.blade.php b/resources/views/email/template/client.blade.php
index ac4dced9248a..93e772abe888 100644
--- a/resources/views/email/template/client.blade.php
+++ b/resources/views/email/template/client.blade.php
@@ -80,6 +80,33 @@
#content .left {
text-align: left !important;
}
+
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: relative;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: relative;
+ }
-
diff --git a/resources/views/pdf-designs/business.html b/resources/views/pdf-designs/business.html
index fe8285c334ac..d4c0da6f2c44 100644
--- a/resources/views/pdf-designs/business.html
+++ b/resources/views/pdf-designs/business.html
@@ -276,6 +276,36 @@
overflow-wrap: break-word;
}
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
+
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/calmness.html b/resources/views/pdf-designs/calm.html
similarity index 83%
rename from resources/views/pdf-designs/calmness.html
rename to resources/views/pdf-designs/calm.html
index 2ec356cecf02..bbd8887835ca 100644
--- a/resources/views/pdf-designs/calmness.html
+++ b/resources/views/pdf-designs/calm.html
@@ -262,7 +262,37 @@
}
[data-ref="total_table-public_notes"] { font-weight: normal; }
+ [data-ref="total_table-terms"] { font-weight: normal; }
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
@@ -290,6 +320,7 @@
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
+
@@ -371,24 +403,23 @@
-
-
-
\ No newline at end of file
+ });
+
+
\ No newline at end of file
diff --git a/resources/views/pdf-designs/clean.html b/resources/views/pdf-designs/clean.html
index b584d6c6be45..1f9fcebbd282 100644
--- a/resources/views/pdf-designs/clean.html
+++ b/resources/views/pdf-designs/clean.html
@@ -257,6 +257,36 @@
overflow-wrap: break-word;
}
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
+
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/creative.html b/resources/views/pdf-designs/creative.html
index 4bf969e71715..1f7f6dbe8050 100644
--- a/resources/views/pdf-designs/creative.html
+++ b/resources/views/pdf-designs/creative.html
@@ -229,6 +229,35 @@
overflow-wrap: break-word;
}
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/elegant.html b/resources/views/pdf-designs/elegant.html
index 98b39ca60bba..649160995e15 100644
--- a/resources/views/pdf-designs/elegant.html
+++ b/resources/views/pdf-designs/elegant.html
@@ -233,7 +233,36 @@
max-width: 300px;
overflow-wrap: break-word;
}
-
+
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/hipster.html b/resources/views/pdf-designs/hipster.html
index b14f8c2fe586..d3201b1bc92c 100644
--- a/resources/views/pdf-designs/hipster.html
+++ b/resources/views/pdf-designs/hipster.html
@@ -251,6 +251,35 @@
overflow-wrap: break-word;
}
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/modern.html b/resources/views/pdf-designs/modern.html
index 4dfea2f4daa7..e47ebffd3db4 100644
--- a/resources/views/pdf-designs/modern.html
+++ b/resources/views/pdf-designs/modern.html
@@ -278,6 +278,35 @@
overflow-wrap: break-word;
}
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/plain.html b/resources/views/pdf-designs/plain.html
index 30acac638bfe..a1f2906f6aff 100644
--- a/resources/views/pdf-designs/plain.html
+++ b/resources/views/pdf-designs/plain.html
@@ -221,6 +221,35 @@
overflow-wrap: break-word;
}
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
diff --git a/resources/views/pdf-designs/playful.html b/resources/views/pdf-designs/playful.html
index 4d308a1a645a..84ac8eb6f294 100644
--- a/resources/views/pdf-designs/playful.html
+++ b/resources/views/pdf-designs/playful.html
@@ -293,6 +293,36 @@
max-width: 300px;
overflow-wrap: break-word;
}
+
+ .stamp {
+ transform: rotate(12deg);
+ color: #555;
+ font-size: 3rem;
+ font-weight: 700;
+ border: 0.25rem solid #555;
+ display: inline-block;
+ padding: 0.25rem 1rem;
+ text-transform: uppercase;
+ border-radius: 1rem;
+ font-family: 'Courier';
+ mix-blend-mode: multiply;
+ z-index:200 !important;
+ position: fixed;
+ text-align: center;
+ }
+
+ .is-paid {
+ color: #D23;
+ border: 1rem double #D23;
+ transform: rotate(-5deg);
+ font-size: 6rem;
+ font-family: "Open sans", Helvetica, Arial, sans-serif;
+ border-radius: 0;
+ padding: 0.5rem;
+ opacity: 0.2;
+ z-index:200 !important;
+ position: fixed;
+ }
/** Useful snippets, uncomment to enable. **/
@@ -323,7 +353,7 @@
/** For more info, please check our docs: https://invoiceninja.github.io **/
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
-
+
@@ -335,7 +365,6 @@
-
|
@@ -380,7 +408,7 @@
-
+