diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index a3b5cf613ecd..ab73fd7bb3a3 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -613,7 +613,6 @@ class CompanySettings extends BaseSettings '$total_taxes', '$line_taxes', '$subtotal', - '$total', '$discount', '$custom_surcharge1', '$custom_surcharge2', @@ -621,6 +620,7 @@ class CompanySettings extends BaseSettings '$custom_surcharge4', '$paid_to_date', '$client.balance', + '$total', ], ]; diff --git a/app/Factory/VendorFactory.php b/app/Factory/VendorFactory.php index 6218200e3232..a618cfc89f80 100644 --- a/app/Factory/VendorFactory.php +++ b/app/Factory/VendorFactory.php @@ -24,13 +24,10 @@ class VendorFactory $vendor->name = ''; $vendor->website = ''; $vendor->private_notes = ''; - $vendor->balance = 0; - $vendor->paid_to_date = 0; + $vendor->public_notes = ''; $vendor->country_id = 4; $vendor->is_deleted = 0; - - $vendor_contact = VendorContactFactory::create($company_id, $user_id); - $vendor->contacts->add($vendor_contact); + $vendor->vendor_hash = Str::random(40); return $vendor; } diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 8801eda1a129..81772a8beed4 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -71,6 +71,7 @@ class BaseController extends Controller 'company.clients.gateway_tokens', 'company.clients.documents', 'company.products', + 'company.products.documents', 'company.recurring_invoices', 'company.invoices.invitations.contact', 'company.invoices.invitations.company', @@ -92,6 +93,7 @@ class BaseController extends Controller 'company.tasks', 'company.projects', 'company.designs', + 'company.documents', 'company.webhooks', 'company.tokens_hashed', ]; @@ -202,7 +204,7 @@ class BaseController extends Controller $query->with( [ 'company' => function ($query) use ($updated_at) { - $query->whereNotNull('updated_at'); + $query->whereNotNull('updated_at')->with('documents'); }, 'company.clients' => function ($query) use ($updated_at) { $query->where('clients.updated_at', '>=', $updated_at)->with('contacts', 'gateway_tokens','documents'); @@ -217,7 +219,7 @@ class BaseController extends Controller $query->whereNotNull('updated_at'); }, 'company.products' => function ($query) use ($updated_at) { - $query->where('updated_at', '>=', $updated_at); + $query->where('updated_at', '>=', $updated_at)->with('documents'); }, 'company.recurring_invoices'=> function ($query) use ($updated_at) { $query->where('updated_at', '>=', $updated_at)->with('company'); diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 09653dfb5bd0..00d97ad933ad 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -11,7 +11,14 @@ namespace App\Http\Controllers; +use App\Factory\ExpenseFactory; use App\Filters\ExpenseFilters; +use App\Http\Requests\Expense\CreateExpenseRequest; +use App\Http\Requests\Expense\DestroyExpenseRequest; +use App\Http\Requests\Expense\EditExpenseRequest; +use App\Http\Requests\Expense\ShowExpenseRequest; +use App\Http\Requests\Expense\StoreExpenseRequest; +use App\Http\Requests\Expense\UpdateExpenseRequest; use App\Jobs\Entity\ActionEntity; use App\Jobs\Util\ProcessBulk; use App\Jobs\Util\UploadAvatar; @@ -266,7 +273,7 @@ class ExpenseController extends BaseController return $request->disallowUpdate(); } - $expense = $this->client_repo->save($request->all(), $expense); + $expense = $this->expense_repo->save($request->all(), $expense); $this->uploadLogo($request->file('company_logo'), $expense->company, $expense); @@ -359,11 +366,7 @@ class ExpenseController extends BaseController */ public function store(StoreExpenseRequest $request) { - $expense = $this->client_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id)); - - $expense->load('contacts', 'primary_contact'); - - $this->uploadLogo($request->file('company_logo'), $expense->company, $expense); + $expense = $this->expense_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id)); return $this->itemResponse($expense); } @@ -485,7 +488,7 @@ class ExpenseController extends BaseController $expenses->each(function ($expense, $key) use ($action) { if (auth()->user()->can('edit', $expense)) { - $this->client_repo->{$action}($expense); + $this->expense_repo->{$action}($expense); } }); diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 97cdf2b47193..32302359ad29 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -700,7 +700,7 @@ class InvoiceController extends BaseController } break; case 'email': - //check query paramater for email_type and set the template else use calculateTemplate + //check query parameter for email_type and set the template else use calculateTemplate if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) { $this->reminder_template = $invoice->client->getSetting(request()->input('email_type')); } else { diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php index 495eab8eff41..c5bcd59a9a5c 100644 --- a/app/Http/Controllers/VendorController.php +++ b/app/Http/Controllers/VendorController.php @@ -11,7 +11,14 @@ namespace App\Http\Controllers; +use App\Factory\VendorFactory; use App\Filters\VendorFilters; +use App\Http\Requests\Vendor\CreateVendorRequest; +use App\Http\Requests\Vendor\DestroyVendorRequest; +use App\Http\Requests\Vendor\EditVendorRequest; +use App\Http\Requests\Vendor\ShowVendorRequest; +use App\Http\Requests\Vendor\StoreVendorRequest; +use App\Http\Requests\Vendor\UpdateVendorRequest; use App\Jobs\Entity\ActionEntity; use App\Jobs\Util\ProcessBulk; use App\Jobs\Util\UploadAvatar; @@ -31,7 +38,6 @@ use Illuminate\Support\Facades\Log; /** * Class VendorController. - * @covers App\Http\Controllers\VendorController */ class VendorController extends BaseController { @@ -266,7 +272,7 @@ class VendorController extends BaseController return $request->disallowUpdate(); } - $vendor = $this->client_repo->save($request->all(), $vendor); + $vendor = $this->vendor_repo->save($request->all(), $vendor); $this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor); @@ -359,7 +365,7 @@ class VendorController extends BaseController */ public function store(StoreVendorRequest $request) { - $vendor = $this->client_repo->save($request->all(), VendorFactory::create(auth()->user()->company()->id, auth()->user()->id)); + $vendor = $this->vendor_repo->save($request->all(), VendorFactory::create(auth()->user()->company()->id, auth()->user()->id)); $vendor->load('contacts', 'primary_contact'); @@ -485,7 +491,7 @@ class VendorController extends BaseController $vendors->each(function ($vendor, $key) use ($action) { if (auth()->user()->can('edit', $vendor)) { - $this->client_repo->{$action}($vendor); + $this->vendor_repo->{$action}($vendor); } }); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 4f69d563d31b..5a5f3802fae9 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -56,7 +56,7 @@ class Kernel extends HttpKernel 'bindings', 'query_logging', \App\Http\Middleware\StartupCheck::class, -// \App\Http\Middleware\Cors::class, + \App\Http\Middleware\Cors::class, ], 'contact' => [ 'throttle:60,1', diff --git a/app/Http/Livewire/RecurringInvoicesTable.php b/app/Http/Livewire/RecurringInvoicesTable.php index 725e861bc4d4..29b548c2aef4 100644 --- a/app/Http/Livewire/RecurringInvoicesTable.php +++ b/app/Http/Livewire/RecurringInvoicesTable.php @@ -19,7 +19,7 @@ class RecurringInvoicesTable extends Component $query = $query ->where('client_id', auth('contact')->user()->client->id) - ->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_COMPLETED]) + ->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_PAUSED,RecurringInvoice::STATUS_COMPLETED]) ->orderBy('status_id', 'asc') ->with('client') ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') diff --git a/app/Http/Middleware/Cors.php b/app/Http/Middleware/Cors.php index d471371215bb..86f10e00493a 100644 --- a/app/Http/Middleware/Cors.php +++ b/app/Http/Middleware/Cors.php @@ -16,7 +16,7 @@ class Cors // ALLOW OPTIONS METHOD $headers = [ 'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE', - 'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range', + 'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range', ]; return Response::make('OK', 200, $headers); diff --git a/app/Http/Requests/Expense/BulkExpenseRequest.php b/app/Http/Requests/Expense/BulkExpenseRequest.php new file mode 100644 index 000000000000..ac0fade1591b --- /dev/null +++ b/app/Http/Requests/Expense/BulkExpenseRequest.php @@ -0,0 +1,47 @@ +has('action')) { + return false; + } + + if (! in_array($this->action, $this->getBulkOptions(), true)) { + return false; + } + + return auth()->user()->can(auth()->user()->isAdmin(), Expense::class); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $rules = $this->getGlobalRules(); + + /* We don't require IDs on bulk storing. */ + if ($this->action !== self::$STORE_METHOD) { + $rules['ids'] = ['required']; + } + + return $rules; + } +} diff --git a/app/Http/Requests/Expense/CreateExpenseRequest.php b/app/Http/Requests/Expense/CreateExpenseRequest.php new file mode 100644 index 000000000000..12f566742015 --- /dev/null +++ b/app/Http/Requests/Expense/CreateExpenseRequest.php @@ -0,0 +1,28 @@ +user()->can('create', Expense::class); + } +} diff --git a/app/Http/Requests/Expense/DestroyExpenseRequest.php b/app/Http/Requests/Expense/DestroyExpenseRequest.php new file mode 100644 index 000000000000..c5ba3194f38d --- /dev/null +++ b/app/Http/Requests/Expense/DestroyExpenseRequest.php @@ -0,0 +1,28 @@ +user()->can('edit', $this->expense); + } +} diff --git a/app/Http/Requests/Expense/EditExpenseRequest.php b/app/Http/Requests/Expense/EditExpenseRequest.php new file mode 100644 index 000000000000..25e4fabba531 --- /dev/null +++ b/app/Http/Requests/Expense/EditExpenseRequest.php @@ -0,0 +1,38 @@ +user()->can('edit', $this->expense); + } + + // public function prepareForValidation() + // { + // $input = $this->all(); + + // //$input['id'] = $this->encodePrimaryKey($input['id']); + + // $this->replace($input); + + // } +} diff --git a/app/Http/Requests/Expense/ShowExpenseRequest.php b/app/Http/Requests/Expense/ShowExpenseRequest.php new file mode 100644 index 000000000000..bf636c1c4a22 --- /dev/null +++ b/app/Http/Requests/Expense/ShowExpenseRequest.php @@ -0,0 +1,28 @@ +user()->can('view', $this->expense); + } +} diff --git a/app/Http/Requests/Expense/StoreExpenseRequest.php b/app/Http/Requests/Expense/StoreExpenseRequest.php new file mode 100644 index 000000000000..22aee8ca7c0a --- /dev/null +++ b/app/Http/Requests/Expense/StoreExpenseRequest.php @@ -0,0 +1,76 @@ +user()->can('create', Expense::class); + } + + public function rules() + { + + /* Ensure we have a client name, and that all emails are unique*/ + //$rules['name'] = 'required|min:1'; + $rules['id_number'] = 'unique:expenses,id_number,'.$this->id.',id,company_id,'.$this->company_id; + //$rules['settings'] = new ValidExpenseGroupSettingsRule(); + $rules['contacts.*.email'] = 'nullable|distinct'; + + $rules['number'] = new UniqueExpenseNumberRule($this->all()); + + // $contacts = request('contacts'); + + // if (is_array($contacts)) { + // for ($i = 0; $i < count($contacts); $i++) { + + // //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct'; + // } + // } + + return $rules; + } + + protected function prepareForValidation() + { + // $input = $this->all(); + + + // $this->replace($input); + } + + public function messages() + { + return [ + 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + //'required' => trans('validation.required', ['attribute' => 'email']), + 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), + ]; + } +} diff --git a/app/Http/Requests/Expense/UpdateExpenseRequest.php b/app/Http/Requests/Expense/UpdateExpenseRequest.php new file mode 100644 index 000000000000..62ca15c72b12 --- /dev/null +++ b/app/Http/Requests/Expense/UpdateExpenseRequest.php @@ -0,0 +1,77 @@ +user()->can('edit', $this->expense); + } + + public function rules() + { + /* Ensure we have a client name, and that all emails are unique*/ + + $rules['country_id'] = 'integer|nullable'; + //$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id; + $rules['contacts.*.email'] = 'nullable|distinct'; + + if ($this->input('number')) { + $rules['number'] = 'unique:expenses,number,'.$this->id.',id,company_id,'.$this->expense->company_id; + } + + $contacts = request('contacts'); + + if (is_array($contacts)) { + // for ($i = 0; $i < count($contacts); $i++) { + // // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id); + // //$rules['contacts.' . $i . '.email'] = 'nullable|email'; + // } + } + + return $rules; + } + + public function messages() + { + return [ + 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + 'email' => ctrans('validation.email', ['attribute' => 'email']), + 'name.required' => ctrans('validation.required', ['attribute' => 'name']), + 'required' => ctrans('validation.required', ['attribute' => 'email']), + ]; + } + + protected function prepareForValidation() + { + $input = $this->all(); + + $this->replace($input); + } +} diff --git a/app/Http/Requests/Vendor/StoreVendorRequest.php b/app/Http/Requests/Vendor/StoreVendorRequest.php index c92c52d65c4a..005fcea8a564 100644 --- a/app/Http/Requests/Vendor/StoreVendorRequest.php +++ b/app/Http/Requests/Vendor/StoreVendorRequest.php @@ -42,27 +42,24 @@ class StoreVendorRequest extends Request //$rules['settings'] = new ValidVendorGroupSettingsRule(); $rules['contacts.*.email'] = 'nullable|distinct'; - $contacts = request('contacts'); + // $contacts = request('contacts'); - if (is_array($contacts)) { - for ($i = 0; $i < count($contacts); $i++) { + // if (is_array($contacts)) { + // for ($i = 0; $i < count($contacts); $i++) { - //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct'; - } - } + // //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct'; + // } + // } return $rules; } protected function prepareForValidation() { - $input = $this->all(); + // $input = $this->all(); - if (! isset($input['settings'])) { - $input['settings'] = VendorSettings::defaults(); - } - $this->replace($input); + // $this->replace($input); } public function messages() diff --git a/app/Http/ValidationRules/Expense/UniqueExpenseNumberRule.php b/app/Http/ValidationRules/Expense/UniqueExpenseNumberRule.php new file mode 100644 index 000000000000..4dfbde5e2298 --- /dev/null +++ b/app/Http/ValidationRules/Expense/UniqueExpenseNumberRule.php @@ -0,0 +1,69 @@ +input = $input; + } + + /** + * @param string $attribute + * @param mixed $value + * @return bool + */ + public function passes($attribute, $value) + { + return $this->checkIfExpenseNumberUnique(); //if it exists, return false! + } + + /** + * @return string + */ + public function message() + { + return 'Expense number already taken'; + } + + /** + * @param $email + * + * //off,when_sent,when_paid + * + * @return bool + */ + private function checkIfExpenseNumberUnique() : bool + { + $expense = Expense::where('client_id', $this->input['client_id']) + ->where('number', $this->input['number']) + ->withTrashed() + ->exists(); + + if ($expense) { + return false; + } + + return true; + } +} diff --git a/app/Jobs/User/UserEmailChanged.php b/app/Jobs/User/UserEmailChanged.php index 6c3cb939b036..8043d4e2232c 100644 --- a/app/Jobs/User/UserEmailChanged.php +++ b/app/Jobs/User/UserEmailChanged.php @@ -92,6 +92,7 @@ class UserEmailChanged extends BaseMailerJob implements ShouldQueue 'button' => ctrans('texts.account_login'), 'signature' => $this->company->owner()->signature, 'logo' => $this->company->present()->logo(), + 'settings' => $this->settings, ]; } } diff --git a/app/Jobs/Util/SendFailedEmails.php b/app/Jobs/Util/SendFailedEmails.php index e9975213e0ef..329ed566c00b 100644 --- a/app/Jobs/Util/SendFailedEmails.php +++ b/app/Jobs/Util/SendFailedEmails.php @@ -56,7 +56,7 @@ class SendFailedEmails implements ShouldQueue private function processEmails() { - \Log::error('processing emails'); + //\Log::error('processing emails'); //info("process emails"); //@todo check that the quota is available for the job diff --git a/app/Mail/Admin/EntitySentObject.php b/app/Mail/Admin/EntitySentObject.php index 5ebcf6a19c64..4367acda86c7 100644 --- a/app/Mail/Admin/EntitySentObject.php +++ b/app/Mail/Admin/EntitySentObject.php @@ -84,6 +84,8 @@ class EntitySentObject 'button' => ctrans("texts.view_{$this->entity_type}"), 'signature' => $settings->email_signature, 'logo' => $this->company->present()->logo(), + 'settings' => $settings, + ]; } } diff --git a/app/Mail/Admin/EntityViewedObject.php b/app/Mail/Admin/EntityViewedObject.php index e491e1566696..367c3f2de2a2 100644 --- a/app/Mail/Admin/EntityViewedObject.php +++ b/app/Mail/Admin/EntityViewedObject.php @@ -84,6 +84,8 @@ class EntityViewedObject 'button' => ctrans("texts.view_{$this->entity_type}"), 'signature' => $settings->email_signature, 'logo' => $this->company->present()->logo(), + 'settings' => $settings, + ]; return $data; diff --git a/app/Mail/Admin/PaymentFailureObject.php b/app/Mail/Admin/PaymentFailureObject.php index 08077206bfdd..12e041740f05 100644 --- a/app/Mail/Admin/PaymentFailureObject.php +++ b/app/Mail/Admin/PaymentFailureObject.php @@ -76,6 +76,8 @@ class PaymentFailureObject ), 'signature' => $signature, 'logo' => $this->company->present()->logo(), + 'settings' => $this->client->getMergedSettings(), + ]; return $data; diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 64de0049e65f..7aadf906fea5 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -36,11 +36,11 @@ class RecurringInvoice extends BaseModel /** * Invoice Statuses. */ - const STATUS_DRAFT = 2; - const STATUS_ACTIVE = 3; - const STATUS_CANCELLED = 4; + const STATUS_DRAFT = 1; + const STATUS_ACTIVE = 2; + const STATUS_PAUSED = 3; + const STATUS_COMPLETED = 4; const STATUS_PENDING = -1; - const STATUS_COMPLETED = -2; /** * Recurring intervals //todo MAP WHEN WE MIGRATE. @@ -102,6 +102,7 @@ class RecurringInvoice extends BaseModel 'frequency_id', 'next_send_date', 'remaining_cycles', + 'auto_bill', ]; protected $casts = [ @@ -189,13 +190,10 @@ class RecurringInvoice extends BaseModel public function getStatusAttribute() { - if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) { //marked as active, but yet to fire first cycle + if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) return self::STATUS_PENDING; - } elseif ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) { - return self::STATUS_COMPLETED; - } else { + else return $this->status_id; - } } public function nextSendDate() :?Carbon @@ -288,16 +286,16 @@ class RecurringInvoice extends BaseModel return '

'.ctrans('texts.draft').'

'; break; case self::STATUS_PENDING: - return '

'.ctrans('texts.sent').'

'; + return '

'.ctrans('texts.pending').'

'; break; case self::STATUS_ACTIVE: - return '

'.ctrans('texts.partial').'

'; + return '

'.ctrans('texts.active').'

'; break; case self::STATUS_COMPLETED: return '

'.ctrans('texts.status_completed').'

'; break; - case self::STATUS_CANCELLED: - return '

'.ctrans('texts.overdue').'

'; + case self::STATUS_PAUSED: + return '

'.ctrans('texts.paused').'

'; break; default: // code... @@ -368,7 +366,7 @@ class RecurringInvoice extends BaseModel /* Return early if nothing to send back! */ if( $this->status_id == self::STATUS_COMPLETED || $this->status_id == self::STATUS_DRAFT || - $this->status_id == self::STATUS_CANCELLED || + $this->status_id == self::STATUS_PAUSED || $this->remaining_cycles == 0 || !$this->next_send_date) { diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 9aae462d21f0..8cd1a54d5686 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -13,6 +13,7 @@ namespace App\Models; use App\Models\Filterable; use App\Models\VendorContact; +use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -21,7 +22,8 @@ class Vendor extends BaseModel { use SoftDeletes; use Filterable; - + use GeneratesCounter; + protected $fillable = [ 'name', 'id_number', @@ -34,6 +36,7 @@ class Vendor extends BaseModel 'postal_code', 'country_id', 'private_notes', + 'public_notes', 'currency_id', 'website', 'transaction_name', @@ -63,6 +66,12 @@ class Vendor extends BaseModel return self::class; } + public function primary_contact() + { + return $this->hasMany(VendorContact::class)->where('is_primary', true); + } + + public function documents() { return $this->morphMany(Document::class, 'documentable'); diff --git a/app/Notifications/Admin/EntitySentNotification.php b/app/Notifications/Admin/EntitySentNotification.php index ec5680a4582a..dead3b4eae8f 100644 --- a/app/Notifications/Admin/EntitySentNotification.php +++ b/app/Notifications/Admin/EntitySentNotification.php @@ -1,4 +1,13 @@ ctrans('texts.view_'.$this->entity_string), 'logo' => $this->entity->company->present()->logo(), 'signature' => $this->settings->email_signature, + 'settings' => $this->settings, ]; diff --git a/app/Notifications/ClientContactRequestCancellation.php b/app/Notifications/ClientContactRequestCancellation.php index 25c2fcce0ddc..927b07e25802 100644 --- a/app/Notifications/ClientContactRequestCancellation.php +++ b/app/Notifications/ClientContactRequestCancellation.php @@ -1,4 +1,13 @@ saveCard($state); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 79542792c87c..ca68f008cef4 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -56,7 +56,8 @@ class AppServiceProvider extends ServiceProvider public function boot() { Relation::morphMap([ - 'invoices' => \App\Models\Invoice::class, + 'invoices' => \App\Models\Invoice::class, + // 'credits' => \App\Models\Credit::class, 'proposals' => \App\Models\Proposal::class, ]); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 8215fb1dc87c..75ff36321ea5 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -225,6 +225,7 @@ class EventServiceProvider extends ServiceProvider ], InvoiceWasPaid::class => [ InvoicePaidActivity::class, + CreateInvoicePdf::class, ], InvoiceWasViewed::class => [ InvoiceViewedActivity::class, @@ -238,6 +239,7 @@ class EventServiceProvider extends ServiceProvider ], InvoiceWasDeleted::class => [ InvoiceDeletedActivity::class, + CreateInvoicePdf::class, ], InvoiceWasArchived::class => [ InvoiceArchivedActivity::class, @@ -247,6 +249,7 @@ class EventServiceProvider extends ServiceProvider ], InvoiceWasReversed::class => [ InvoiceReversedActivity::class, + CreateInvoicePdf::class, ], InvoiceWasCancelled::class => [ InvoiceCancelledActivity::class, diff --git a/app/Repositories/VendorContactRepository.php b/app/Repositories/VendorContactRepository.php index ba831d338a50..f9aab86646eb 100644 --- a/app/Repositories/VendorContactRepository.php +++ b/app/Repositories/VendorContactRepository.php @@ -13,6 +13,7 @@ namespace App\Repositories; use App\Models\Vendor; use App\Models\VendorContact; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; /** @@ -58,9 +59,17 @@ class VendorContactRepository extends BaseRepository $update_contact->fill($contact); + if (array_key_exists('password', $contact) && strlen($contact['password']) > 1) { + + $update_contact->password = Hash::make($contact['password']); + + } + $update_contact->save(); }); + $vendor->load('contacts'); + //always made sure we have one blank contact to maintain state if ($contacts->count() == 0) { $new_contact = new VendorContact; diff --git a/app/Services/Invoice/HandleReversal.php b/app/Services/Invoice/HandleReversal.php index 5ee609a06086..7a991f80ba09 100644 --- a/app/Services/Invoice/HandleReversal.php +++ b/app/Services/Invoice/HandleReversal.php @@ -18,6 +18,7 @@ use App\Factory\InvoiceItemFactory; use App\Factory\PaymentFactory; use App\Helpers\Invoice\InvoiceSum; use App\Models\Client; +use App\Models\Credit; use App\Models\Invoice; use App\Models\Payment; use App\Models\Paymentable; @@ -55,7 +56,7 @@ class HandleReversal extends AbstractService $total_paid = $this->invoice->amount - $this->invoice->balance; /*Adjust payment applied and the paymentables to the correct amount */ - $paymentables = Paymentable::wherePaymentableType(Invoice::class) + $paymentables = Paymentable::wherePaymentableType('invoices') ->wherePaymentableId($this->invoice->id) ->get(); @@ -95,6 +96,23 @@ class HandleReversal extends AbstractService $credit->service()->markSent()->save(); } + /*If there is a payment linked, then the credit needs to be linked back to that payment in case of refund*/ + if($paymentables->count() > 0){ + + $payment = $paymentables->first()->payment; + $payment->credits()->save($credit); + + $paymentable_credit = $payment->credits() + ->wherePaymentableType(Credit::class) + ->wherePaymentableId($credit->id) + ->first(); + + //harvest the credit record and add in the amount for the credit. + $paymentable_credit->pivot->amount = $total_paid; + $paymentable_credit->pivot->save(); + + } + /* Set invoice balance to 0 */ if ($this->invoice->balance != 0) { $this->invoice->ledger()->updateInvoiceBalance($balance_remaining * -1, $notes)->save(); @@ -119,37 +137,4 @@ class HandleReversal extends AbstractService //create a ledger row for this with the resulting Credit ( also include an explanation in the notes section ) } - // public function run2() - // { - - // /* Check again!! */ - // if (!$this->invoice->invoiceReversable($this->invoice)) { - // return $this->invoice; - // } - - // if($this->invoice->status_id == Invoice::STATUS_CANCELLED) - // $this->invoice = $this->invoice->service()->reverseCancellation()->save(); - - // //$balance_remaining = $this->invoice->balance; - - // //$total_paid = $this->invoice->amount - $this->invoice->balance; - - // /*Adjust payment applied and the paymentables to the correct amount */ - // $paymentables = Paymentable::wherePaymentableType(Invoice::class) - // ->wherePaymentableId($this->invoice->id) - // ->get(); - - // $total_paid = 0; - - // $paymentables->each(function ($paymentable) use ($total_paid) { - - // $reversable_amount = $paymentable->amount - $paymentable->refunded; - // $total_paid -= $reversable_amount; - // $paymentable->amount = $paymentable->refunded; - // $paymentable->save(); - // }); - - // //Unwinding any payments made to this invoice - - // } } diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 9e027a98f381..a7e52f7d63ce 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -206,6 +206,9 @@ class InvoiceService if($this->invoice->balance == 0) $this->setStatus(Invoice::STATUS_PAID); + if($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) + $this->setStatus(Invoice::STATUS_PARTIAL); + return $this; } diff --git a/app/Services/Payment/UpdateInvoicePayment.php b/app/Services/Payment/UpdateInvoicePayment.php index ed6c69d316c2..341e3c746595 100644 --- a/app/Services/Payment/UpdateInvoicePayment.php +++ b/app/Services/Payment/UpdateInvoicePayment.php @@ -73,6 +73,8 @@ class UpdateInvoicePayment $pivot_invoice->pivot->amount = $paid_amount; $pivot_invoice->pivot->save(); + $this->payment->applied += $paid_amount; + $invoice->service() //caution what if we amount paid was less than partial - we wipe it! ->clearPartial() ->updateBalance($paid_amount * -1) @@ -82,6 +84,8 @@ class UpdateInvoicePayment event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars())); }); + + $this->payment->save(); return $this->payment; } diff --git a/app/Transformers/VendorTransformer.php b/app/Transformers/VendorTransformer.php index a5eb2a03aa3a..26cfc5e83c4b 100644 --- a/app/Transformers/VendorTransformer.php +++ b/app/Transformers/VendorTransformer.php @@ -76,6 +76,7 @@ class VendorTransformer extends EntityTransformer 'name' => $vendor->name ?: '', 'website' => $vendor->website ?: '', 'private_notes' => $vendor->private_notes ?: '', + 'public_notes' => $vendor->public_notes ?: '', 'last_login' => (int) $vendor->last_login, 'address1' => $vendor->address1 ?: '', 'address2' => $vendor->address2 ?: '', diff --git a/app/Utils/ClientPortal/CustomMessage/CustomMessage.php b/app/Utils/ClientPortal/CustomMessage/CustomMessage.php index 649370b62f0f..ca298cf3a701 100644 --- a/app/Utils/ClientPortal/CustomMessage/CustomMessage.php +++ b/app/Utils/ClientPortal/CustomMessage/CustomMessage.php @@ -1,4 +1,13 @@ resetCompanyCounters($vendor->company); + + $counter = $vendor->company->settings->vendor_number_counter; + $setting_entity = $vendor->company->settings->vendor_number_counter; + + $vendor_number = $this->checkEntityNumber(Vendor::class, $vendor, $counter, $vendor->company->settings->counter_padding, $vendor->company->settings->vendor_number_pattern); + + $this->incrementCounter($vendor->company, 'vendor_number_counter'); + + return $vendor_number; + } + + /** * Determines if it has shared counter. * @@ -254,33 +278,29 @@ trait GeneratesCounter * Checks that the number has not already been used. * * @param Collection $entity The entity ie App\Models\Client, Invoice, Quote etc - * @param int $counter The counter - * @param int $padding The padding + * @param int $counter The counter + * @param int $padding The padding * - * @return string The padded and prefixed invoice number + * @return string The padded and prefixed entity number */ - private function checkEntityNumber($class, $client, $counter, $padding, $pattern) + private function checkEntityNumber($class, $entity, $counter, $padding, $pattern) { $check = false; do { $number = $this->padCounter($counter, $padding); - $number = $this->applyNumberPattern($client, $number, $pattern); - - if ($class == Invoice::class || $class == RecurringInvoice::class) { - $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first(); - } elseif ($class == Client::class) { - $check = $class::whereCompanyId($client->company_id)->whereIdNumber($number)->withTrashed()->first(); - } elseif ($class == Credit::class) { - $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first(); - } elseif ($class == Quote::class) { - $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first(); - } elseif ($class == Payment::class) { - $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first(); - } + $number = $this->applyNumberPattern($entity, $number, $pattern); + if ($class == Invoice::class || $class == RecurringInvoice::class) + $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first(); + elseif ($class == Client::class || $class == Vendor::class) + $check = $class::whereCompanyId($entity->company_id)->whereIdNumber($number)->withTrashed()->first(); + else + $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first(); + $counter++; + } while ($check); return $number; @@ -389,16 +409,74 @@ trait GeneratesCounter $client->company->save(); } + private function resetCompanyCounters($company) + { + $timezone = Timezone::find($company->settings->timezone_id); + + $reset_date = Carbon::parse($company->settings->reset_counter_date, $timezone->name); + + if (! $reset_date->isToday() || ! $company->settings->reset_counter_date) { + return false; + } + + switch ($company->reset_counter_frequency_id) { + case RecurringInvoice::FREQUENCY_WEEKLY: + $reset_date->addWeek(); + break; + case RecurringInvoice::FREQUENCY_TWO_WEEKS: + $reset_date->addWeeks(2); + break; + case RecurringInvoice::FREQUENCY_FOUR_WEEKS: + $reset_date->addWeeks(4); + break; + case RecurringInvoice::FREQUENCY_MONTHLY: + $reset_date->addMonth(); + break; + case RecurringInvoice::FREQUENCY_TWO_MONTHS: + $reset_date->addMonths(2); + break; + case RecurringInvoice::FREQUENCY_THREE_MONTHS: + $reset_date->addMonths(3); + break; + case RecurringInvoice::FREQUENCY_FOUR_MONTHS: + $reset_date->addMonths(4); + break; + case RecurringInvoice::FREQUENCY_SIX_MONTHS: + $reset_date->addMonths(6); + break; + case RecurringInvoice::FREQUENCY_ANNUALLY: + $reset_date->addYear(); + break; + case RecurringInvoice::FREQUENCY_TWO_YEARS: + $reset_date->addYears(2); + break; + } + + $settings = $company->settings; + $settings->reset_counter_date = $reset_date->format('Y-m-d'); + $settings->invoice_number_counter = 1; + $settings->quote_number_counter = 1; + $settings->credit_number_counter = 1; + $settings->vendor_number_counter = 1; + $settings->ticket_number_counter = 1; + $settings->payment_number_counter = 1; + $settings->task_number_counter = 1; + $settings->expense_number_counter = 1; + + $company->settings = $settings; + $company->save(); + } + /** - * { function_description }. + * Formats a entity number by pattern * - * @param \App\Models\Client $client The client - * @param string $counter The counter - * @param null|string $pattern The pattern + * @param \App\Models\BaseModel $entity The entity object + * @param string $counter The counter + * @param null|string $pattern The pattern * - * @return string ( description_of_the_return_value ) + * @return string The formatted number pattern */ - private function applyNumberPattern(Client $client, string $counter, $pattern) :string + private function applyNumberPattern($entity, string $counter, $pattern) :string { if (! $pattern) { return $counter; @@ -417,7 +495,7 @@ trait GeneratesCounter $replace[] = $counter; if (strstr($pattern, '{$user_id}')) { - $user_id = $client->user_id ? $client->user_id : 0; + $user_id = $entity->user_id ? $entity->user_id : 0; $search[] = '{$user_id}'; $replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT); } @@ -429,24 +507,24 @@ trait GeneratesCounter $search[] = $matches[0]; /* The following adjusts for the company timezone - may bork tests depending on the time of day the tests are run!!!!!!*/ - $date = Carbon::now($client->company->timezone()->name)->format($format); + $date = Carbon::now($entity->company->timezone()->name)->format($format); $replace[] = str_replace($format, $date, $matches[1]); } $search[] = '{$custom1}'; - $replace[] = $client->custom_value1; + $replace[] = $entity->custom_value1; $search[] = '{$custom2}'; - $replace[] = $client->custom_value2; + $replace[] = $entity->custom_value2; $search[] = '{$custom3}'; - $replace[] = $client->custom_value3; + $replace[] = $entity->custom_value3; $search[] = '{$custom4}'; - $replace[] = $client->custom_value4; + $replace[] = $entity->custom_value4; $search[] = '{$id_number}'; - $replace[] = $client->id_number; + $replace[] = $entity->id_number; return str_replace($search, $replace, $pattern); } diff --git a/database/migrations/2020_09_22_205113_id_number_fields_for_missing_entities.php b/database/migrations/2020_09_22_205113_id_number_fields_for_missing_entities.php new file mode 100644 index 000000000000..a1e665959d6a --- /dev/null +++ b/database/migrations/2020_09_22_205113_id_number_fields_for_missing_entities.php @@ -0,0 +1,45 @@ +string('number')->nullable(); + }); + + Schema::table('tasks', function (Blueprint $table) { + $table->string('number')->nullable(); + }); + + Schema::table('vendors', function (Blueprint $table) { + $table->text('vendor_hash')->nullable(); + $table->text('public_notes')->nullable(); + }); + + Schema::table('vendor_contacts', function (Blueprint $table) { + $table->boolean('send_email')->default(0); + }); + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/public/js/clients/payments/checkout.com.js b/public/js/clients/payments/checkout.com.js index 3fbd93d87430..70d0db5931e3 100644 --- a/public/js/clients/payments/checkout.com.js +++ b/public/js/clients/payments/checkout.com.js @@ -1,2 +1,2 @@ /*! For license information please see checkout.com.js.LICENSE.txt */ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=9)}({9:function(e,t,n){e.exports=n("XYrq")},XYrq:function(e,t){window.CKOConfig={publicKey:document.querySelector('meta[name="public-key"]').content,customerEmail:document.querySelector('meta[name="customer-email"]').content,value:document.querySelector('meta[name="value"]').content,currency:document.querySelector('meta[name="currency"]').content,paymentMode:"cards",cardFormMode:"cardTokenisation",cardTokenised:function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.data),document.querySelector('input[name="store_card"]').value=document.getElementById("store-card-checkbox").checked?1:0,document.getElementById("server-response").submit()}}}}); \ No newline at end of file +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=9)}({9:function(e,t,n){e.exports=n("XYrq")},XYrq:function(e,t){window.CKOConfig={publicKey:document.querySelector('meta[name="public-key"]').content,customerEmail:document.querySelector('meta[name="customer-email"]').content,value:document.querySelector('meta[name="value"]').content,currency:document.querySelector('meta[name="currency"]').content,paymentMode:"cards",cardFormMode:"cardTokenisation",cardTokenised:function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.data),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}}}}); \ No newline at end of file diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 3949c1d94763..ea07f12968c8 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -9,7 +9,7 @@ "/js/clients/payments/alipay.js": "/js/clients/payments/alipay.js?id=766690d4207c0d8e75d1", "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=914bbac0c52e458a55ea", "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=7200ac43b87bddf1bedc", - "/js/clients/payments/checkout.com.js": "/js/clients/payments/checkout.com.js?id=42d239882b80af83ad22", + "/js/clients/payments/checkout.com.js": "/js/clients/payments/checkout.com.js?id=bee8407bd5dfae80dc6a", "/js/clients/payments/process.js": "/js/clients/payments/process.js?id=8f0a87e44b6e6824c010", "/js/clients/payments/sofort.js": "/js/clients/payments/sofort.js?id=ca99b358b094202d1e3a", "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1702ddb9f59521c7758f", diff --git a/resources/js/clients/payment_methods/authorize-authorize-card.js b/resources/js/clients/payment_methods/authorize-authorize-card.js index 89b45482041b..9af8543c825d 100644 --- a/resources/js/clients/payment_methods/authorize-authorize-card.js +++ b/resources/js/clients/payment_methods/authorize-authorize-card.js @@ -66,11 +66,6 @@ class AuthorizeAuthorizeCard { } handle() { - //this.handleFormValidation(); - - // At this point as an small API you can request this.form.valid to check if input elements are valid. - // Note: this.form.valid will not handle empty fields. - this.cardButton.addEventListener("click", () => { this.cardButton.disabled = !this.cardButton.disabled; this.handleAuthorization(); diff --git a/resources/js/clients/payments/checkout.com.js b/resources/js/clients/payments/checkout.com.js index 1d4d4de59002..f00797a9628b 100644 --- a/resources/js/clients/payments/checkout.com.js +++ b/resources/js/clients/payments/checkout.com.js @@ -23,7 +23,7 @@ window.CKOConfig = { document.querySelector( 'input[name="store_card"]' - ).value = document.getElementById('store-card-checkbox').checked ? 1 : 0; + ).value = document.querySelector('input[name=token-billing-checkbox]:checked').value; document.getElementById('server-response').submit(); }, diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 0628fa616320..ea2eec4f6e60 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3277,4 +3277,6 @@ return [ 'under_payments_disabled' => 'Company doesn\'t support under payments.', 'over_payments_disabled' => 'Company doesn\'t support over payments.', + + 'paused' => 'Paused', ]; diff --git a/resources/views/portal/ninja2020/gateways/authorize/credit_card_payment.blade.php b/resources/views/portal/ninja2020/gateways/authorize/credit_card_payment.blade.php index c03aa8e185fb..661bba3e330e 100644 --- a/resources/views/portal/ninja2020/gateways/authorize/credit_card_payment.blade.php +++ b/resources/views/portal/ninja2020/gateways/authorize/credit_card_payment.blade.php @@ -58,14 +58,32 @@ @include('portal.ninja2020.gateways.authorize.credit_card') - -
+ +
{{ ctrans('texts.token_billing_checkbox') }}
- + +
diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php index 0c0f85f5fdd7..4c97ad520b6f 100644 --- a/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php @@ -84,12 +84,31 @@
@else -
-
+
+
{{ ctrans('texts.token_billing_checkbox') }}
- + +
diff --git a/resources/views/portal/ninja2020/gateways/stripe/add_credit_card.blade.php b/resources/views/portal/ninja2020/gateways/stripe/add_credit_card.blade.php index 5532e5730ee7..587ea00a4414 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/add_credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/add_credit_card.blade.php @@ -22,7 +22,7 @@

{{ ctrans('texts.add_credit_card') }}

-

+

{{ ctrans('texts.authorize_for_future_use') }}

@@ -43,18 +43,30 @@
-
+
- {{ ctrans('texts.save_as_default') }} + {{ ctrans('texts.token_billing_checkbox') }}
-
diff --git a/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php index 77c91ba8e5c3..e2b40fe4f885 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php @@ -31,6 +31,28 @@
+ +
+
+ {{ ctrans('texts.subtotal') }} +
+
+ {{ App\Utils\Number::formatMoney($total['invoice_totals'], $client) }} +
+
+ {{ ctrans('texts.gateway_fees') }} +
+
+ {{ App\Utils\Number::formatMoney($total['fee_total'], $client) }} +
+
+ {{ ctrans('texts.total') }} +
+
+ {{ App\Utils\Number::formatMoney($total['amount_with_fee'], $client) }} +
+
+ @if($token)
@@ -65,6 +87,7 @@ placeholder="{{ ctrans('texts.name') }}">
+
{{ ctrans('texts.credit_card') }} @@ -73,14 +96,35 @@
-
+ +
{{ ctrans('texts.token_billing_checkbox') }}
- + +
+