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 **/ +
@@ -301,7 +332,7 @@
-
+
@@ -316,7 +347,7 @@
-

$entity_label

+

$entity_label

@@ -343,6 +374,7 @@
+
@@ -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 @@
-
@@ -367,8 +396,7 @@
-
-
+
- +