diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 650b687250ba..892ab4b01a6b 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -18,7 +18,7 @@ jobs: phpunit-versions: ['latest'] ci_node_total: [ 8 ] ci_node_index: [ 0, 1, 2, 3, 4, 5, 6, 7] - laravel: [10.*] + laravel: [11.*] dependency-version: [prefer-stable] env: @@ -103,7 +103,6 @@ jobs: restore-keys: | ${{ runner.os }}-${{ matrix.php }}-composer- - - name: Install composer dependencies run: | composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} diff --git a/app/DataMapper/BaseSettings.php b/app/DataMapper/BaseSettings.php index 4cc61589abb9..882f1ed0ba8b 100644 --- a/app/DataMapper/BaseSettings.php +++ b/app/DataMapper/BaseSettings.php @@ -16,13 +16,6 @@ namespace App\DataMapper; */ class BaseSettings { - // //@deprecated - // public function __construct($obj) - // { - // // foreach ($obj as $key => $value) { - // // $obj->{$key} = $value; - // // } - // } public static function setCasts($obj, $casts) { diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 2e201f637fe9..3aaf31106c3d 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -522,8 +522,6 @@ class CompanySettings extends BaseSettings public string $email_template_payment_failed = ''; public static $casts = [ - 'email_template_payment_failed' => 'string', - 'email_subject_payment_failed' => 'string', 'payment_flow' => 'string', 'enable_quote_reminder1' => 'bool', 'quote_num_days_reminder1' => 'int', @@ -774,6 +772,8 @@ class CompanySettings extends BaseSettings 'portal_custom_js' => 'string', 'client_portal_enable_uploads' => 'bool', 'purchase_order_number_counter' => 'integer', + 'email_template_payment_failed' => 'string', + 'email_subject_payment_failed' => 'string', ]; public static $free_plan_casts = [ diff --git a/app/DataMapper/EmailTemplateDefaults.php b/app/DataMapper/EmailTemplateDefaults.php index 0d34fcfe4897..24579e9fa244 100644 --- a/app/DataMapper/EmailTemplateDefaults.php +++ b/app/DataMapper/EmailTemplateDefaults.php @@ -140,7 +140,7 @@ class EmailTemplateDefaults public static function emailPaymentFailedTemplate() { - return '

$client

'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'

$payment_error

$payment_button
'; + return '

$client

'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'

$payment_error

$view_button
'; } public static function emailQuoteReminder1Subject() diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 1e8de4773eda..547c7dfcb540 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -48,6 +48,9 @@ class BaseRule implements RuleInterface 'DK', // Denmark 'EE', // Estonia 'ES', // Spain + 'ES-CN', // Canary Islands + 'ES-CE', // Ceuta + 'ES-ML', // Melilla 'FI', // Finland 'FR', // France 'GR', // Greece @@ -78,6 +81,9 @@ class BaseRule implements RuleInterface 'DK' => 'EU', // Denmark 'EE' => 'EU', // Estonia 'ES' => 'EU', // Spain + 'ES-CN' => 'EU', // Canary Islands + 'ES-CE' => 'EU', // Ceuta + 'ES-ML' => 'EU', // Melilla 'FI' => 'EU', // Finland 'FR' => 'EU', // France 'GR' => 'EU', // Greece diff --git a/app/DataMapper/Tax/ES_CE/Rule.php b/app/DataMapper/Tax/ES_CE/Rule.php new file mode 100644 index 000000000000..24086d297df6 --- /dev/null +++ b/app/DataMapper/Tax/ES_CE/Rule.php @@ -0,0 +1,44 @@ +version = 'beta'; } + if($this->version == 'beta') { + + //CEUTA + $this->regions->EU->subregions->{'ES-CE'} = new \stdClass(); + $this->regions->EU->subregions->{'ES-CE'}->tax_rate = 4; + $this->regions->EU->subregions->{'ES-CE'}->tax_name = 'IGIC'; + $this->regions->EU->subregions->{'ES-CE'}->reduced_tax_rate = 4; + $this->regions->EU->subregions->{'ES-CE'}->apply_tax = false; + + //MELILLA ML 4 + $this->regions->EU->subregions->{'ES-ML'} = new \stdClass(); + $this->regions->EU->subregions->{'ES-ML'}->tax_rate = 4; + $this->regions->EU->subregions->{'ES-ML'}->tax_name = 'IGIC'; + $this->regions->EU->subregions->{'ES-ML'}->reduced_tax_rate = 4; + $this->regions->EU->subregions->{'ES-ML'}->apply_tax = false; + + //CANARIAS CN 7/3 + $this->regions->EU->subregions->{'ES-CN'} = new \stdClass(); + $this->regions->EU->subregions->{'ES-CN'}->tax_rate = 7; + $this->regions->EU->subregions->{'ES-CN'}->tax_name = 'IGIC'; + $this->regions->EU->subregions->{'ES-CN'}->reduced_tax_rate = 4; + $this->regions->EU->subregions->{'ES-CN'}->apply_tax = false; + + $this->version = 'gamma'; + } + return $this; } @@ -419,6 +445,25 @@ class TaxModel $this->regions->EU->subregions->ES->reduced_tax_rate = 10; $this->regions->EU->subregions->ES->apply_tax = false; + $this->regions->EU->subregions->{'ES-CE'} = new \stdClass(); + $this->regions->EU->subregions->{'ES-CE'}->tax_rate = 4; + $this->regions->EU->subregions->{'ES-CE'}->tax_name = 'IGIC'; + $this->regions->EU->subregions->{'ES-CE'}->reduced_tax_rate = 4; + $this->regions->EU->subregions->{'ES-CE'}->apply_tax = false; + + $this->regions->EU->subregions->{'ES-ML'} = new \stdClass(); + $this->regions->EU->subregions->{'ES-ML'}->tax_rate = 4; + $this->regions->EU->subregions->{'ES-ML'}->tax_name = 'IGIC'; + $this->regions->EU->subregions->{'ES-ML'}->reduced_tax_rate = 4; + $this->regions->EU->subregions->{'ES-ML'}->apply_tax = false; + + $this->regions->EU->subregions->{'ES-CN'} = new \stdClass(); + $this->regions->EU->subregions->{'ES-CN'}->tax_rate = 7; + $this->regions->EU->subregions->{'ES-CN'}->tax_name = 'IGIC'; + $this->regions->EU->subregions->{'ES-CN'}->reduced_tax_rate = 3; + $this->regions->EU->subregions->{'ES-CN'}->apply_tax = false; + + $this->regions->EU->subregions->FI = new \stdClass(); $this->regions->EU->subregions->FI->tax_rate = 24; $this->regions->EU->subregions->FI->tax_name = 'ALV'; diff --git a/app/Enum/HttpVerb.php b/app/Enum/HttpVerb.php new file mode 100644 index 000000000000..ef7572cacc3a --- /dev/null +++ b/app/Enum/HttpVerb.php @@ -0,0 +1,22 @@ +field = $field; + + // Ensure that everything is assigned properly by calling the parent constructor + parent::__construct($message, $code, $previous); + } + + public function getInvalidField() + { + return $this->field; + } +} diff --git a/app/Export/CSV/ActivityExport.php b/app/Export/CSV/ActivityExport.php index bdbc232f6e4c..f9f60bbcdc61 100644 --- a/app/Export/CSV/ActivityExport.php +++ b/app/Export/CSV/ActivityExport.php @@ -101,8 +101,11 @@ class ActivityExport extends BaseExport $t = app('translator'); $t->replace(Ninja::transformTranslations($this->company->settings)); - $this->date_format = DateFormat::find($this->company->settings->date_format_id)->format; + /** @var \App\Models\DateFormat $df */ + $df = DateFormat::query()->find($this->company->settings->date_format_id); + $this->date_format = $df->format; + if (count($this->input['report_keys']) == 0) { $this->input['report_keys'] = array_values($this->entity_keys); } diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index 9d4bcd2bd4ec..a8fed6fff95c 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -845,7 +845,7 @@ class BaseExport /** * Apply Product Filters * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * * @return Builder */ @@ -870,7 +870,7 @@ class BaseExport /** * Add Client Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param mixed $clients * * @return Builder @@ -893,7 +893,7 @@ class BaseExport /** * Add Vendor Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $vendors * * @return Builder @@ -917,7 +917,7 @@ class BaseExport /** * AddProjectFilter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $projects * * @return Builder @@ -941,7 +941,7 @@ class BaseExport /** * Add Category Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $expense_categories * * @return Builder @@ -966,7 +966,7 @@ class BaseExport /** * Add Payment Status Filters * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $status * * @return Builder @@ -1024,10 +1024,10 @@ class BaseExport /** * Add RecurringInvoice Status Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $status * - * @return Builder + * @return \Illuminate\Database\Eloquent\Builder */ protected function addRecurringInvoiceStatusFilter(Builder $query, string $status): Builder { @@ -1067,7 +1067,7 @@ class BaseExport /** * Add QuoteStatus Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $status * * @return Builder @@ -1133,7 +1133,7 @@ class BaseExport /** * Add PurchaseOrder Status Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $status * * @return Builder @@ -1183,7 +1183,7 @@ class BaseExport /** * Add Invoice Status Filter * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param string $status * @return Builder */ @@ -1249,7 +1249,7 @@ class BaseExport /** * Add Date Range * - * @param Builder $query + * @param \Illuminate\Database\Eloquent\Builder $query * @param ?string $table_name * @return Builder */ diff --git a/app/Filters/BankTransactionFilters.php b/app/Filters/BankTransactionFilters.php index f786f49e78ed..2471f98aaa25 100644 --- a/app/Filters/BankTransactionFilters.php +++ b/app/Filters/BankTransactionFilters.php @@ -68,7 +68,7 @@ class BankTransactionFilters extends QueryFilters */ public function client_status(string $value = ''): Builder { - if (strlen($value) == 0) { + if (strlen($value ?? '') == 0) { return $this->builder; } @@ -108,13 +108,24 @@ class BankTransactionFilters extends QueryFilters } if (count($debit_or_withdrawal_array) >= 1) { - $query->orWhereIn('base_type', $debit_or_withdrawal_array); + $query->whereIn('base_type', $debit_or_withdrawal_array); } }); return $this->builder; } + public function active_banks(string $value = ''): Builder + { + + if (strlen($value) == 0 || $value != 'true') { + return $this->builder; + } + + return $this->builder->whereHas('bank_integration', function ($query){ + $query->where('is_deleted', 0)->whereNull('deleted_at'); + }); + } /** * Filters the list based on Bank Accounts. diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index d1d7dd56cff0..15a9782e52d5 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -42,6 +42,9 @@ class InvoiceItemSum 'DK', // Denmark 'EE', // Estonia 'ES', // Spain + 'ES-CE', + 'ES-CN', + 'ES-ML', 'FI', // Finland 'FR', // France 'GR', // Greece @@ -72,6 +75,9 @@ class InvoiceItemSum 'DK', // Denmark 'EE', // Estonia 'ES', // Spain + 'ES-CE', + 'ES-CN', + 'ES-ML', 'FI', // Finland 'FR', // France 'GR', // Greece @@ -182,7 +188,7 @@ class InvoiceItemSum /** @var \App\DataMapper\Tax\BaseRule $class */ - $class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule"; + $class = "App\DataMapper\Tax\\".str_replace("-","_",$this->client->company->country()->iso_3166_2)."\\Rule"; $this->rule = new $class(); diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index 2a22627d8491..1f9555523b97 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -86,6 +86,9 @@ class InvoiceController extends Controller return render('invoices.show-fullscreen', $data); } + if(!$invoice->isPayable()) + return $this->render('invoices.show',$data); + return auth()->guard('contact')->user()->client->getSetting('payment_flow') == 'default' ? $this->render('invoices.show', $data) : $this->render('invoices.show_smooth', $data); // return $this->render('invoices.show_smooth', $data); diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 3ab4a714413b..0c119a58bff1 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -108,12 +108,6 @@ class PaymentController extends Controller */ public function process(Request $request) { - // $request->validate([ - // 'contact_first_name' => ['required'], - // 'contact_last_name' => ['required'], - // 'contact_email' => ['required', 'email'], - // ]); - return (new InstantPayment($request))->run(); } @@ -123,13 +117,7 @@ class PaymentController extends Controller $gateway = CompanyGateway::findOrFail($request->input('company_gateway_id')); $payment_hash = PaymentHash::with('fee_invoice')->where('hash', $request->payment_hash)->firstOrFail(); - // if($payment_hash) $invoice = $payment_hash->fee_invoice; - // else - // $invoice = Invoice::with('client')->where('id',$payment_hash->fee_invoice_id)->orderBy('id','desc')->first(); - - // $invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id); - $client = $invoice ? $invoice->client : auth()->guard('contact')->user()->client; diff --git a/app/Http/Controllers/ClientPortal/PaymentMethodController.php b/app/Http/Controllers/ClientPortal/PaymentMethodController.php index f85d9dbc9483..24a19a662b68 100644 --- a/app/Http/Controllers/ClientPortal/PaymentMethodController.php +++ b/app/Http/Controllers/ClientPortal/PaymentMethodController.php @@ -56,8 +56,8 @@ class PaymentMethodController extends Controller $data['gateway'] = $gateway; - /** @var \App\Models\ClientContact auth()->user() **/ - $client_contact = auth()->user(); + /** @var \App\Models\ClientContact auth()->guard('contact')->user() **/ + $client_contact = auth()->guard('contact')->user(); $data['client'] = $client_contact->client; return $gateway @@ -77,8 +77,8 @@ class PaymentMethodController extends Controller { $gateway = $this->getClientGateway(); - /** @var \App\Models\ClientContact auth()->user() **/ - $client_contact = auth()->user(); + /** @var \App\Models\ClientContact auth()->guard('contact')->user() **/ + $client_contact = auth()->guard('contact')->user(); return $gateway ->driver($client_contact->client) @@ -103,8 +103,8 @@ class PaymentMethodController extends Controller public function verify(ClientGatewayToken $payment_method) { - /** @var \App\Models\ClientContact auth()->user() **/ - $client_contact = auth()->user(); + /** @var \App\Models\ClientContact auth()->guard('contact')->user() **/ + $client_contact = auth()->guard('contact')->user(); return $payment_method->gateway ->driver($client_contact->client) @@ -114,8 +114,8 @@ class PaymentMethodController extends Controller public function processVerification(Request $request, ClientGatewaytoken $payment_method) { - /** @var \App\Models\ClientContact auth()->user() **/ - $client_contact = auth()->user(); + /** @var \App\Models\ClientContact auth()->guard('contact')->user() **/ + $client_contact = auth()->guard('contact')->user(); return $payment_method->gateway ->driver($client_contact->client) @@ -131,8 +131,8 @@ class PaymentMethodController extends Controller */ public function destroy(ClientGatewayToken $payment_method) { - /** @var \App\Models\ClientContact auth()->user() **/ - $client_contact = auth()->user(); + /** @var \App\Models\ClientContact auth()->guard('contact')->user() **/ + $client_contact = auth()->guard('contact')->user(); if ($payment_method->gateway()->exists()) { $payment_method->gateway @@ -145,9 +145,19 @@ class PaymentMethodController extends Controller event(new MethodDeleted($payment_method, auth()->guard('contact')->user()->company, Ninja::eventVars(auth()->guard('contact')->user()->id))); $payment_method->is_deleted = true; + $payment_method->is_default = false; $payment_method->delete(); $payment_method->save(); + + $def_cgt = auth()->guard('contact')->user()->client->gateway_tokens()->orderBy('id','desc')->first(); + + if($def_cgt) + { + $def_cgt->is_default = true; + $def_cgt->save(); + } + } catch (Exception $e) { nlog($e->getMessage()); @@ -161,8 +171,8 @@ class PaymentMethodController extends Controller private function getClientGateway() { - /** @var \App\Models\ClientContact auth()->user() **/ - $client_contact = auth()->user(); + /** @var \App\Models\ClientContact auth()->guard('contact')->user() **/ + $client_contact = auth()->guard('contact')->user(); if (request()->query('method') == GatewayType::CREDIT_CARD) { return $client_contact->client->getCreditCardGateway(); diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 776390a4016a..d6e28367d117 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -150,6 +150,10 @@ class DocumentController extends BaseController $document->fill($request->all()); $document->save(); + if($document->documentable) { //@phpstan-ignore-line + $document->documentable->touch(); + } + return $this->itemResponse($document->fresh()); } diff --git a/app/Http/Controllers/EInvoiceController.php b/app/Http/Controllers/EInvoiceController.php new file mode 100644 index 000000000000..1904ce5f2123 --- /dev/null +++ b/app/Http/Controllers/EInvoiceController.php @@ -0,0 +1,39 @@ +entity){ + 'invoices' => $data = $el->checkInvoice($request->getEntity()), + 'clients' => $data = $el->checkClient($request->getEntity()), + 'companies' => $data = $el->checkCompany($request->getEntity()), + default => $data['passes'] = false, + }; + + nlog($data); + + return response()->json($data, $data['passes'] ? 200 : 400); + + } + +} diff --git a/app/Http/Controllers/Gateways/GoCardlessController.php b/app/Http/Controllers/Gateways/GoCardlessController.php index 10ba88b915e0..dff6ca5c0d4b 100644 --- a/app/Http/Controllers/Gateways/GoCardlessController.php +++ b/app/Http/Controllers/Gateways/GoCardlessController.php @@ -20,6 +20,7 @@ class GoCardlessController extends Controller { public function ibpRedirect(IbpRequest $request) { + return $request ->getCompanyGateway() ->driver($request->getClient()) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index ee363bb808a3..b7d72affb9f9 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -475,7 +475,10 @@ class InvoiceController extends BaseController */ public function destroy(DestroyInvoiceRequest $request, Invoice $invoice) { - $this->invoice_repo->delete($invoice); + + if (!$invoice->is_deleted) { + $this->invoice_repo->delete($invoice); + } return $this->itemResponse($invoice->fresh()); } diff --git a/app/Http/Controllers/OneTimeTokenController.php b/app/Http/Controllers/OneTimeTokenController.php index d4aa685c58b6..489297bb20f7 100644 --- a/app/Http/Controllers/OneTimeTokenController.php +++ b/app/Http/Controllers/OneTimeTokenController.php @@ -73,6 +73,10 @@ class OneTimeTokenController extends BaseController 'is_react' => $request->hasHeader('X-REACT') ? true : false, ]; + if($request->institution_id) { + $data['institution_id'] = $request->institution_id; + } + Cache::put($hash, $data, 3600); return response()->json(['hash' => $hash], 200); diff --git a/app/Http/Requests/Activity/StoreNoteRequest.php b/app/Http/Requests/Activity/StoreNoteRequest.php index 250fcfceec46..54c7c79180a5 100644 --- a/app/Http/Requests/Activity/StoreNoteRequest.php +++ b/app/Http/Requests/Activity/StoreNoteRequest.php @@ -17,6 +17,7 @@ use Illuminate\Validation\Rule; class StoreNoteRequest extends Request { + /** * Determine if the user is authorized to make this request. * diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index ea41667db8ce..c936d23c2d6c 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -136,6 +136,10 @@ class UpdateClientRequest extends Request $input['shipping_country_id'] = $this->getCountryCode($input['shipping_country_code']); } + if (isset($input['e_invoice']) && is_array($input['e_invoice'])) { + //ensure it is normalized first! + $input['e_invoice'] = $this->client->filterNullsRecursive($input['e_invoice']); + } $this->replace($input); } diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php index 1d26fb044b0d..de12d404af5c 100644 --- a/app/Http/Requests/Company/UpdateCompanyRequest.php +++ b/app/Http/Requests/Company/UpdateCompanyRequest.php @@ -130,6 +130,11 @@ class UpdateCompanyRequest extends Request $input['smtp_verify_peer'] == 'true' ? true : false; } + if (isset($input['e_invoice']) && is_array($input['e_invoice'])) { + //ensure it is normalized first! + $input['e_invoice'] = $this->company->filterNullsRecursive($input['e_invoice']); + } + $this->replace($input); } diff --git a/app/Http/Requests/Credit/StoreCreditRequest.php b/app/Http/Requests/Credit/StoreCreditRequest.php index 626ee07fdc48..86cb9e1f2166 100644 --- a/app/Http/Requests/Credit/StoreCreditRequest.php +++ b/app/Http/Requests/Credit/StoreCreditRequest.php @@ -107,6 +107,7 @@ class StoreCreditRequest extends Request $input = $this->decodePrimaryKeys($input); $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $input['line_items'] = $this->cleanFeeItems($input['line_items']); $input['amount'] = $this->entityTotalAmount($input['line_items']); if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) { diff --git a/app/Http/Requests/EInvoice/ValidateEInvoiceRequest.php b/app/Http/Requests/EInvoice/ValidateEInvoiceRequest.php new file mode 100644 index 000000000000..b40aa92c3dcd --- /dev/null +++ b/app/Http/Requests/EInvoice/ValidateEInvoiceRequest.php @@ -0,0 +1,93 @@ +user(); + + $entity = $this->getEntity(); + + if($entity instanceof Company) + return $entity->id == $user->company()->id; + + return $user->can('view', $entity); + + } + + public function rules() + { + + /** @var \App\Models\User $user */ + $user = auth()->user(); + + return [ + 'entity' => 'required|bail|in:invoices,clients,companies', + 'entity_id' => ['required','bail', Rule::exists($this->entity, 'id') + ->when($this->entity != 'companies', function ($q) use($user){ + $q->where('company_id', $user->company()->id); + }) + ], + ]; + } + + public function prepareForValidation() + { + $input = $this->all(); + + if (isset($input['entity_id']) && $input['entity_id'] != null) { + $input['entity_id'] = $this->decodePrimaryKey($input['entity_id']); + } + + + $this->replace($input); + } + + public function getEntity() + { + if(!$this->entity) { + return false; + } + + + $class = Invoice::class; + + match ($this->entity) { + 'invoices' => $class = Invoice::class, + 'clients' => $class = Client::class, + 'companies' => $class = Company::class, + default => $class = Invoice::class, + }; + + if($this->entity == 'companies') + return auth()->user()->company(); + + return $class::withTrashed()->find(is_string($this->entity_id) ? $this->decodePrimaryKey($this->entity_id) : $this->entity_id); + + } +} diff --git a/app/Http/Requests/Invoice/DestroyInvoiceRequest.php b/app/Http/Requests/Invoice/DestroyInvoiceRequest.php index ff2e39eee287..0da03a17b9c2 100644 --- a/app/Http/Requests/Invoice/DestroyInvoiceRequest.php +++ b/app/Http/Requests/Invoice/DestroyInvoiceRequest.php @@ -24,4 +24,23 @@ class DestroyInvoiceRequest extends Request { return auth()->user()->can('edit', $this->invoice); } + + + public function rules() + { + return []; + } + + public function prepareForValidation() + { + + /** @var \App\Models\User $user */ + $user = auth()->user(); + + if(\Illuminate\Support\Facades\Cache::has($this->ip()."|".$this->invoice->id."|".$user->company()->company_key)) + throw new \App\Exceptions\DuplicatePaymentException('Duplicate request.', 429); + + \Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->invoice->id."|".$user->company()->company_key), true, 1); + + } } diff --git a/app/Http/Requests/Invoice/StoreInvoiceRequest.php b/app/Http/Requests/Invoice/StoreInvoiceRequest.php index f76174d0ca9b..4617dd77340a 100644 --- a/app/Http/Requests/Invoice/StoreInvoiceRequest.php +++ b/app/Http/Requests/Invoice/StoreInvoiceRequest.php @@ -108,6 +108,7 @@ class StoreInvoiceRequest extends Request if (isset($input['line_items']) && is_array($input['line_items'])) { $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $input['line_items'] = $this->cleanFeeItems($input['line_items']); $input['amount'] = $this->entityTotalAmount($input['line_items']); } if(isset($input['partial']) && $input['partial'] == 0) { diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php index 1dee8fac1a1a..bde91d3d08a7 100644 --- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php @@ -121,6 +121,11 @@ class UpdateInvoiceRequest extends Request $client = \App\Models\Client::withTrashed()->find($input['client_id']); $input['due_date'] = \Illuminate\Support\Carbon::parse($input['date'])->addDays((int)$client->getSetting('payment_terms'))->format('Y-m-d'); } + + if (isset($input['e_invoice']) && is_array($input['e_invoice'])) { + //ensure it is normalized first! + $input['e_invoice'] = $this->invoice->filterNullsRecursive($input['e_invoice']); + } $this->replace($input); } diff --git a/app/Http/Requests/Payment/RefundPaymentRequest.php b/app/Http/Requests/Payment/RefundPaymentRequest.php index 75171f27b0e2..4b54e7228621 100644 --- a/app/Http/Requests/Payment/RefundPaymentRequest.php +++ b/app/Http/Requests/Payment/RefundPaymentRequest.php @@ -70,8 +70,8 @@ class RefundPaymentRequest extends Request 'id' => ['bail','required', new ValidRefundableRequest($input)], 'amount' => ['numeric', 'max:99999999999999'], 'date' => 'required', - 'invoices.*.invoice_id' => 'required', - 'invoices.*.amount' => 'required', + 'invoices.*.invoice_id' => 'required|bail', + 'invoices.*.amount' => 'required|bail|gt:0', 'invoices' => new ValidRefundableInvoices($input), ]; diff --git a/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php index 27e6b1b8c7ad..8ab1e534de9e 100644 --- a/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php +++ b/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php @@ -95,6 +95,7 @@ class StorePurchaseOrderRequest extends Request if (isset($input['line_items']) && is_array($input['line_items'])) { $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $input['line_items'] = $this->cleanFeeItems($input['line_items']); $input['amount'] = $this->entityTotalAmount($input['line_items']); } diff --git a/app/Http/Requests/Quote/StoreQuoteRequest.php b/app/Http/Requests/Quote/StoreQuoteRequest.php index 47264fc28d10..fb6f70a5121f 100644 --- a/app/Http/Requests/Quote/StoreQuoteRequest.php +++ b/app/Http/Requests/Quote/StoreQuoteRequest.php @@ -101,6 +101,7 @@ class StoreQuoteRequest extends Request if (isset($input['line_items']) && is_array($input['line_items'])) { $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $input['line_items'] = $this->cleanFeeItems($input['line_items']); $input['amount'] = $this->entityTotalAmount($input['line_items']); } if(isset($input['partial']) && $input['partial'] == 0) { diff --git a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php index 3bdecfa1e66e..2ff1df711c96 100644 --- a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php @@ -148,6 +148,8 @@ class StoreRecurringInvoiceRequest extends Request } $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; + $input['line_items'] = $this->cleanFeeItems($input['line_items']); + $input['amount'] = $this->entityTotalAmount($input['line_items']); if (isset($input['auto_bill'])) { diff --git a/app/Jobs/Subscription/CleanStaleInvoiceOrder.php b/app/Jobs/Subscription/CleanStaleInvoiceOrder.php index 9795d08cd2c7..fccc11b7ee40 100644 --- a/app/Jobs/Subscription/CleanStaleInvoiceOrder.php +++ b/app/Jobs/Subscription/CleanStaleInvoiceOrder.php @@ -59,7 +59,7 @@ class CleanStaleInvoiceOrder implements ShouldQueue Invoice::query() ->withTrashed() - ->where('status_id', Invoice::STATUS_SENT) + ->where('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->where('updated_at', '<', now()->subHour()) ->where('balance', '>', 0) ->whereJsonContains('line_items', ['type_id' => '3']) diff --git a/app/Listeners/Invoice/InvoiceRestoredActivity.php b/app/Listeners/Invoice/InvoiceRestoredActivity.php index c9276046bee1..2d2f15071a8b 100644 --- a/app/Listeners/Invoice/InvoiceRestoredActivity.php +++ b/app/Listeners/Invoice/InvoiceRestoredActivity.php @@ -45,7 +45,7 @@ class InvoiceRestoredActivity implements ShouldQueue $fields = new stdClass(); - $user_id = isset($event->event_vars['user_id']) ? $event->event_vars['user_id'] : $event->invitation->invoice->user_id; + $user_id = isset($event->event_vars['user_id']) ? $event->event_vars['user_id'] : $event->invoice->user_id; $fields->user_id = $user_id; $fields->invoice_id = $event->invoice->id; diff --git a/app/Livewire/Flow2/InvoicePay.php b/app/Livewire/Flow2/InvoicePay.php index 2c2aecc86e28..811b16838567 100644 --- a/app/Livewire/Flow2/InvoicePay.php +++ b/app/Livewire/Flow2/InvoicePay.php @@ -41,7 +41,7 @@ class InvoicePay extends Component 'client_postal_code' => 'postal_code', 'client_country_id' => 'country_id', - 'client_shipping_address_line_1' => 'shipping_address1', + 'client_shipping_address_line_1' => 'shipping_address1', 'client_shipping_address_line_2' => 'shipping_address2', 'client_shipping_city' => 'shipping_city', 'client_shipping_state' => 'shipping_state', @@ -128,6 +128,8 @@ class InvoicePay extends Component public function payableAmount($payable_amount) { // $this->setContext('payable_invoices.0.amount', Number::parseFloat($payable_amount)); // $this->context['payable_invoices'][0]['amount'] = Number::parseFloat($payable_amount); //TODO DB: check parseFloat() + + $this->setContext('amount', $payable_amount); $this->under_over_payment = false; } @@ -188,7 +190,7 @@ class InvoicePay extends Component } } } - + return $this->required_fields = false; } @@ -196,6 +198,7 @@ class InvoicePay extends Component #[Computed()] public function component(): string { + if (!$this->terms_accepted) { return Terms::class; } @@ -217,6 +220,7 @@ class InvoicePay extends Component } return ProcessPayment::class; + } #[Computed()] @@ -239,12 +243,9 @@ class InvoicePay extends Component $this->setContext('settings', $settings); // $this->context['settings'] = $settings; $this->setContext('db', $this->db); // $this->context['db'] = $this->db; - nlog($this->invoices); - - if(is_array($this->invoices)) { + if(is_array($this->invoices)) $this->invoices = Invoice::find($this->transformKeys($this->invoices)); - } - + $invoices = $this->invoices->filter(function ($i) { $i = $i->service() ->markSent() @@ -273,11 +274,14 @@ class InvoicePay extends Component 'invoice_id' => $i->hashed_id, 'amount' => $i->partial > 0 ? $i->partial : $i->balance, 'formatted_amount' => Number::formatValue($i->partial > 0 ? $i->partial : $i->balance, $i->client->currency()), + 'formatted_currency' => Number::formatMoney($i->partial > 0 ? $i->partial : $i->balance, $i->client), 'number' => $i->number, - 'date' => $i->translateDate($i->date, $i->client->date_format(), $i->client->locale()) + 'date' => $i->translateDate($i->date, $i->client->date_format(), $i->client->locale()), + 'due_date' => $i->translateDate($i->due_date, $i->client->date_format(), $i->client->locale()) ]; })->toArray(); + $this->setContext('amount', array_sum(array_column($payable_invoices, 'amount'))); $this->setContext('payable_invoices', $payable_invoices); } @@ -285,4 +289,13 @@ class InvoicePay extends Component { return render('flow2.invoice-pay'); } + + public function exception($e, $stopPropagation) + { + + nlog($e->getMessage()); + + $stopPropagation(); + + } } diff --git a/app/Livewire/Flow2/InvoiceSummary.php b/app/Livewire/Flow2/InvoiceSummary.php index ce84992512a1..af21a26894a6 100644 --- a/app/Livewire/Flow2/InvoiceSummary.php +++ b/app/Livewire/Flow2/InvoiceSummary.php @@ -12,9 +12,10 @@ namespace App\Livewire\Flow2; -use App\Utils\Traits\WithSecureContext; -use Livewire\Attributes\On; +use App\Utils\Number; use Livewire\Component; +use Livewire\Attributes\On; +use App\Utils\Traits\WithSecureContext; class InvoiceSummary extends Component { @@ -22,26 +23,64 @@ class InvoiceSummary extends Component public $invoices; + public $amount; + public function mount() { //@TODO for a single invoice - show all details, for multi-invoices, only show the summaries - $this->invoices = $this->getContext()['invoices']; // $this->context['invitation']->invoice; + // $this->invoices = $this->getContext()['invoices']; // $this->context['invitation']->invoice; + + $contact = $this->getContext()['contact']; + $this->invoices = $this->getContext()['payable_invoices']; + $this->amount = Number::formatMoney($this->getContext()['amount'], $contact->client); + } #[On(self::CONTEXT_UPDATE)] public function onContextUpdate(): void { // refactor logic for updating the price for eg if it changes with under/over pay + $contact = $this->getContext()['contact']; + $this->invoices = $this->getContext()['payable_invoices']; + $this->amount = Number::formatMoney($this->getContext()['amount'], $contact->client); + + // $this->invoices = $this->getContext()['invoices']; + } + + + public function downloadDocument($invoice_hashed_id) + { + nlog("here"); + $contact = $this->getContext()['contact']; + $_invoices = $this->getContext()['invoices']; + $i = $_invoices->first(function ($i) use($invoice_hashed_id){ + return $i->hashed_id == $invoice_hashed_id; + }); + + $file_name = $i->numberFormatter().'.pdf'; + + $file = (new \App\Jobs\Entity\CreateRawPdf($i->invitations()->where('client_contact_id', $contact->id)->first()))->handle(); + + + nlog("here"); + + nlog($file); + + $headers = ['Content-Type' => 'application/pdf']; + + return response()->streamDownload(function () use ($file) { + echo $file; + }, $file_name, $headers); - $this->invoices = $this->getContext()['invoices']; } public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View { + $contact = $this->getContext()['contact']; + return render('flow2.invoices-summary', [ - 'invoice' => $this->invoices, - 'client' => $this->invoices->first()->client, + 'client' => $contact->client, ]); - + } } diff --git a/app/Livewire/Flow2/PaymentMethod.php b/app/Livewire/Flow2/PaymentMethod.php index 2014159e98be..19986f73676f 100644 --- a/app/Livewire/Flow2/PaymentMethod.php +++ b/app/Livewire/Flow2/PaymentMethod.php @@ -75,4 +75,13 @@ class PaymentMethod extends Component { return render('flow2.payment-method', ['methods' => $this->methods]); } + + public function exception($e, $stopPropagation) + { + + nlog($e->getMessage()); + + $stopPropagation(); + + } } diff --git a/app/Livewire/Flow2/ProcessPayment.php b/app/Livewire/Flow2/ProcessPayment.php index a761b8440dab..c162ba1ee14f 100644 --- a/app/Livewire/Flow2/ProcessPayment.php +++ b/app/Livewire/Flow2/ProcessPayment.php @@ -29,7 +29,7 @@ class ProcessPayment extends Component private array $payment_data_payload = []; public $isLoading = true; - + public function mount() { @@ -64,7 +64,7 @@ class ProcessPayment extends Component ->setPaymentHash($responder_data['payload']['ph']); $this->payment_data_payload = $driver->processPaymentViewData($responder_data['payload']); - + $this->payment_view = $driver->livewirePaymentView( $this->payment_data_payload, ); @@ -83,4 +83,21 @@ class ProcessPayment extends Component return render($this->payment_view, $this->payment_data_payload); } + + public function exception($e, $stopPropagation) + { + + $errors = session()->get('errors', new \Illuminate\Support\ViewErrorBag()); + + $bag = new \Illuminate\Support\MessageBag(); + $bag->add('gateway_error', $e->getMessage()); + + session()->put('errors', $errors->put('default', $bag)); + + $invoice_id = $this->getContext()['payable_invoices'][0]['invoice_id']; + $this->redirectRoute('client.invoice.show', ['invoice' => $invoice_id]); + $stopPropagation(); + + } + } diff --git a/app/Livewire/Flow2/RequiredFields.php b/app/Livewire/Flow2/RequiredFields.php index e3a31830755c..9460aaeeaafe 100644 --- a/app/Livewire/Flow2/RequiredFields.php +++ b/app/Livewire/Flow2/RequiredFields.php @@ -44,6 +44,8 @@ class RequiredFields extends Component public ?string $client_custom_value3; public ?string $client_custom_value4; + public $contact; + /** @var array */ public array $fields = []; @@ -63,7 +65,7 @@ class RequiredFields extends Component ->with('company') ->find($this->getContext()['company_gateway_id']); - $contact = auth()->user(); + $contact = auth()->guard('contact')->user(); $this->client_name = $contact->client->name; $this->contact_first_name = $contact->first_name; @@ -111,10 +113,10 @@ class RequiredFields extends Component $rff = new RFFService( fields: $this->fields, database: $this->getContext()['db'], - company_gateway_id: (string) $this->company_gateway->id, + company_gateway_id: (string)$this->company_gateway->id, ); - $contact = auth()->user(); + $contact = auth()->guard('contact')->user(); /** @var \App\Models\ClientContact $contact */ $errors = $rff->handleSubmit($data, $contact, return_errors: true, callback: function () { @@ -133,4 +135,5 @@ class RequiredFields extends Component 'contact' => $this->getContext()['contact'], ]); } + } diff --git a/app/Livewire/Flow2/UnderOverPayment.php b/app/Livewire/Flow2/UnderOverPayment.php index 93865ee6a0bd..52c257deb315 100644 --- a/app/Livewire/Flow2/UnderOverPayment.php +++ b/app/Livewire/Flow2/UnderOverPayment.php @@ -18,6 +18,7 @@ use Livewire\Component; class UnderOverPayment extends Component { + use WithSecureContext; public $payableAmount; @@ -44,28 +45,30 @@ class UnderOverPayment extends Component $settings = $this->getContext()['settings']; - foreach($payableInvoices as $key => $invoice) { + foreach($payableInvoices as $key => $invoice){ $payableInvoices[$key]['amount'] = Number::parseFloat($invoice['formatted_amount']); + $payableInvoices[$key]['formatted_currency'] = Number::FormatMoney($payableInvoices[$key]['amount'], $this->getContext()['invitation']->contact->client); } $input_amount = collect($payableInvoices)->sum('amount'); - if($settings->client_portal_allow_under_payment && $settings->client_portal_under_payment_minimum != 0) { - if($input_amount <= $settings->client_portal_under_payment_minimum) { + if($settings->client_portal_allow_under_payment && $settings->client_portal_under_payment_minimum != 0) + { + if($input_amount <= $settings->client_portal_under_payment_minimum){ // return error message under payment too low. $this->errors = ctrans('texts.minimum_required_payment', ['amount' => $settings->client_portal_under_payment_minimum]); $this->dispatch('errorMessageUpdate', errors: $this->errors); } } - if(!$settings->client_portal_allow_over_payment && ($input_amount > $this->invoice_amount)) { + if(!$settings->client_portal_allow_over_payment && ($input_amount > $this->invoice_amount)){ $this->errors = ctrans('texts.over_payments_disabled'); $this->dispatch('errorMessageUpdate', errors: $this->errors); } - if(!$this->errors) { + if(!$this->errors){ $this->setContext('payable_invoices', $payableInvoices); - $this->dispatch('payable-amount', payable_amount: $input_amount); + $this->dispatch('payable-amount', payable_amount: $input_amount ); } } diff --git a/app/Mail/Subscription/OtpCode.php b/app/Mail/Subscription/OtpCode.php index 1f624b3a3781..beecd91302b2 100644 --- a/app/Mail/Subscription/OtpCode.php +++ b/app/Mail/Subscription/OtpCode.php @@ -51,14 +51,14 @@ class OtpCode extends Mailable return $this->from(config('mail.from.address'), config('mail.from.name')) ->subject(ctrans('texts.otp_code_subject')) ->text('email.admin.generic_text') - ->view('email.admin.generic') + ->view('email.client.generic') ->with([ 'settings' => $this->company->settings, 'logo' => $this->company->present()->logo(), 'title' => ctrans('texts.otp_code_subject'), 'content' => ctrans('texts.otp_code_body', ['code' => $this->code]), 'whitelabel' => $this->company->account->isPaid(), - 'url' => '', + 'url' => false, 'button' => false, 'template' => $this->company->account->isPremium() ? 'email.template.admin_premium' : 'email.template.admin', ]); diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 175940b1f6eb..46378651e39e 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -306,19 +306,19 @@ class BaseModel extends Model /** - * arrayFilterRecursive + * arrayFilterRecursive nee filterNullsRecursive * * Removes null properties from an array * * @param array $array * @return array */ - public function arrayFilterRecursive(array $array): array + public function filterNullsRecursive(array $array): array { foreach ($array as $key => $value) { if (is_array($value)) { // Recursively filter the nested array - $array[$key] = $this->arrayFilterRecursive($value); + $array[$key] = $this->filterNullsRecursive($value); } // Remove null values if (is_null($array[$key])) { diff --git a/app/Models/Client.php b/app/Models/Client.php index 26c8ee79e6ab..cbb7500daa29 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -661,7 +661,8 @@ class Client extends BaseModel implements HasLocalePreference } } - if ($this->currency()->code == 'CAD' && in_array(GatewayType::ACSS, array_column($pms, 'gateway_type_id'))) { + if (in_array($this->currency()->code, ['CAD','USD']) && in_array(GatewayType::ACSS, array_column($pms, 'gateway_type_id'))) { + // if ($this->currency()->code == 'CAD' && in_array(GatewayType::ACSS, array_column($pms, 'gateway_type_id'))) { foreach ($pms as $pm) { if ($pm['gateway_type_id'] == GatewayType::ACSS) { $cg = CompanyGateway::query()->find($pm['company_gateway_id']); @@ -673,6 +674,20 @@ class Client extends BaseModel implements HasLocalePreference } } + + if (in_array($this->currency()->code, ['GBP']) && in_array(GatewayType::BACS, array_column($pms, 'gateway_type_id'))) { + // if ($this->currency()->code == 'CAD' && in_array(GatewayType::ACSS, array_column($pms, 'gateway_type_id'))) { + foreach ($pms as $pm) { + if ($pm['gateway_type_id'] == GatewayType::BACS) { + $cg = CompanyGateway::query()->find($pm['company_gateway_id']); + + if ($cg && $cg->fees_and_limits->{GatewayType::BACS}->is_enabled) { + return $cg; + } + } + } + } + return null; } diff --git a/app/Models/Company.php b/app/Models/Company.php index 052040cc164c..97a567d4ef89 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -475,7 +475,7 @@ class Company extends BaseModel public function bank_transactions(): HasMany { - return $this->hasMany(BankTransaction::class); + return $this->hasMany(BankTransaction::class)->withTrashed(); } public function bank_transaction_rules(): HasMany diff --git a/app/Models/Document.php b/app/Models/Document.php index 140c98cdf656..9019c3442968 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -85,6 +85,10 @@ class Document extends BaseModel 'deleted_at' => 'timestamp', ]; + protected $touches = [ + // 'documentable' + ]; + /** * @var array */ diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index 8260e71efa6e..0c259bf4d546 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -21,6 +21,7 @@ use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\AuthorizePaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\Utils\Traits\MakesHash; use net\authorize\api\contract\v1\DeleteCustomerPaymentProfileRequest; use net\authorize\api\contract\v1\DeleteCustomerProfileRequest; @@ -30,7 +31,7 @@ use net\authorize\api\controller\DeleteCustomerProfileController; /** * Class AuthorizeCreditCard. */ -class AuthorizeCreditCard +class AuthorizeCreditCard implements LivewireMethodInterface { use MakesHash; @@ -41,7 +42,7 @@ class AuthorizeCreditCard $this->authorize = $authorize; } - public function processPaymentView($data) + public function paymentData(array $data): array { $tokens = ClientGatewayToken::where('client_id', $this->authorize->client->id) ->where('company_gateway_id', $this->authorize->company_gateway->id) @@ -54,6 +55,13 @@ class AuthorizeCreditCard $data['public_client_id'] = $this->authorize->init()->getPublicClientKey(); $data['api_login_id'] = $this->authorize->company_gateway->getConfigField('apiLoginId'); + return $data; + } + + public function processPaymentView($data) + { + $data = $this->paymentData($data); + return render('gateways.authorize.credit_card.pay', $data); } @@ -313,4 +321,9 @@ class AuthorizeCreditCard 'invoices' => $vars['invoices'], ]; } + + public function livewirePaymentView(array $data): string + { + return 'gateways.authorize.credit_card.pay_livewire'; + } } diff --git a/app/PaymentDrivers/BTCPay/BTCPay.php b/app/PaymentDrivers/BTCPay/BTCPay.php index 8b5b3a07aff8..99d35c729f29 100644 --- a/app/PaymentDrivers/BTCPay/BTCPay.php +++ b/app/PaymentDrivers/BTCPay/BTCPay.php @@ -14,6 +14,7 @@ namespace App\PaymentDrivers\BTCPay; use App\Models\Payment; use App\PaymentDrivers\BTCPayPaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\Utils\Traits\MakesHash; use App\PaymentDrivers\Common\MethodInterface; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; @@ -24,7 +25,7 @@ use App\Services\Email\EmailObject; use App\Services\Email\Email; use Illuminate\Support\Facades\App; -class BTCPay implements MethodInterface +class BTCPay implements MethodInterface, LivewireMethodInterface { use MakesHash; @@ -49,9 +50,7 @@ class BTCPay implements MethodInterface public function paymentView($data) { - $data['gateway'] = $this->driver_class; - $data['amount'] = $data['total']['amount_with_fee']; - $data['currency'] = $this->driver_class->client->getCurrencyCode(); + $data = $this->paymentData($data); return render('gateways.btcpay.pay', $data); } @@ -176,4 +175,24 @@ class BTCPay implements MethodInterface throw new PaymentFailed('Error during BTCPay refund : ' . $e->getMessage()); } } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.btcpay.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->driver_class; + $data['amount'] = $data['total']['amount_with_fee']; + $data['currency'] = $this->driver_class->client->getCurrencyCode(); + + return $data; + } } diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 3dca62962f52..ff89084efc6e 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -404,14 +404,18 @@ class BaseDriver extends AbstractPaymentDriver $item->gross_line_total = round($item->gross_line_total, 2); return $item; }) - ->whereIn('type_id', ['3','4']) - ->where('gross_line_total', round($fee_total,2)) + ->whereIn('type_id', ['3']) + ->where('gross_line_total', '<=', round($fee_total,2)) ->count(); if($invoice && $fee_count == 0){ nlog("apparently no fee, so injecting here!"); + if(!$invoice->uses_inclusive_taxes){ //must account for taxes! ? line item taxes also + $fee_total = round($fee_total/(1 + (($invoice->tax_rate1+$invoice->tax_rate2+$invoice->tax_rate3)/100)),2); + } + $balance = $invoice->balance; App::forgetInstance('translator'); @@ -426,7 +430,7 @@ class BaseDriver extends AbstractPaymentDriver $invoice_item->quantity = 1; $invoice_item->cost = (float)$fee_total; - $invoice_items = (array) $invoice->line_items; + $invoice_items = $invoice->line_items; $invoice_items[] = $invoice_item; if (isset($data['gateway_type_id']) && $fees_and_limits = $this->company_gateway->getFeesAndLimits($data['gateway_type_id'])) { @@ -501,28 +505,28 @@ class BaseDriver extends AbstractPaymentDriver */ public function storeGatewayToken(array $data, array $additional = []): ?ClientGatewayToken { - $company_gateway_token = new ClientGatewayToken(); - $company_gateway_token->company_id = $this->client->company->id; - $company_gateway_token->client_id = $this->client->id; - $company_gateway_token->token = $data['token']; - $company_gateway_token->company_gateway_id = $this->company_gateway->id; - $company_gateway_token->gateway_type_id = $data['payment_method_id']; - $company_gateway_token->meta = $data['payment_meta']; + $cgt = new ClientGatewayToken(); + $cgt->company_id = $this->client->company->id; + $cgt->client_id = $this->client->id; + $cgt->token = $data['token']; + $cgt->company_gateway_id = $this->company_gateway->id; + $cgt->gateway_type_id = $data['payment_method_id']; + $cgt->meta = $data['payment_meta']; foreach ($additional as $key => $value) { - $company_gateway_token->{$key} = $value; + $cgt->{$key} = $value; } - $company_gateway_token->save(); + $cgt->save(); - if ($this->client->gateway_tokens->count() == 1) { + if ($this->client->gateway_tokens->count() > 1) { $this->client->gateway_tokens()->update(['is_default' => 0]); - - $company_gateway_token->is_default = 1; - $company_gateway_token->save(); } - return $company_gateway_token; + $cgt->is_default = 1; + $cgt->save(); + + return $cgt; } public function processInternallyFailedPayment($gateway, $e) @@ -639,7 +643,7 @@ class BaseDriver extends AbstractPaymentDriver $message = [ 'server_response' => $response, - 'data' => $this->payment_hash->data, + 'data' => $this->payment_hash?->data, ]; SystemLogger::dispatch( @@ -656,6 +660,16 @@ class BaseDriver extends AbstractPaymentDriver } } + public function livewirePaymentView(array $data): string + { + return $this->payment_method->livewirePaymentView($data); + } + + public function processPaymentViewData(array $data): array + { + return $this->payment_method->paymentData($data); + } + public function checkRequirements() { if ($this->company_gateway->require_billing_address) { diff --git a/app/PaymentDrivers/Braintree/ACH.php b/app/PaymentDrivers/Braintree/ACH.php index bbd854291d71..df705328dfe3 100644 --- a/app/PaymentDrivers/Braintree/ACH.php +++ b/app/PaymentDrivers/Braintree/ACH.php @@ -12,19 +12,23 @@ namespace App\PaymentDrivers\Braintree; use App\Exceptions\PaymentFailed; +use App\Http\Controllers\ClientPortal\InvoiceController; +use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Jobs\Util\SystemLogger; use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\Models\Payment; +use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\BraintreePaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; -class ACH implements MethodInterface +class ACH implements MethodInterface, LivewireMethodInterface { use MakesHash; @@ -52,7 +56,7 @@ class ACH implements MethodInterface } public function authorizeResponse(Request $request) - { + { $request->validate([ 'nonce' => ['required'], 'gateway_type_id' => ['required'], @@ -86,6 +90,22 @@ class ACH implements MethodInterface $this->braintree->storeGatewayToken($data, ['gateway_customer_reference' => $customer->id]); + if ($request->authorize_then_redirect) { + $this->braintree->payment_hash = PaymentHash::where('hash', $request->payment_hash)->firstOrFail(); + + $data = [ + 'invoices' => collect($this->braintree->payment_hash->data->invoices)->map(fn ($invoice) => $invoice->invoice_id)->toArray(), + 'action' => 'payment', + ]; + + $request = new ProcessInvoicesInBulkRequest(); + $request->replace($data); + + session()->flash('message', ctrans('texts.payment_method_added')); + + return app(InvoiceController::class)->bulk($request); + } + return redirect()->route('client.payment_methods.index')->withMessage(ctrans('texts.payment_method_added')); } catch (\Exception $e) { return $this->braintree->processInternallyFailedPayment($this->braintree, $e); @@ -97,10 +117,11 @@ class ACH implements MethodInterface public function paymentView(array $data) { - $data['gateway'] = $this->braintree; - $data['currency'] = $this->braintree->client->getCurrencyCode(); - $data['payment_method_id'] = GatewayType::BANK_TRANSFER; - $data['amount'] = $this->braintree->payment_hash->data->amount_with_fee; + $data = $this->paymentData($data); + + if (array_key_exists('authorize_then_redirect', $data)) { + return render('gateways.braintree.ach.authorize', array_merge($data)); + } return render('gateways.braintree.ach.pay', $data); } @@ -181,4 +202,34 @@ class ACH implements MethodInterface throw new PaymentFailed($response->transaction->additionalProcessorResponse, $response->transaction->processorResponseCode); } + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + if (array_key_exists('authorize_then_redirect', $data)) { + return 'gateways.braintree.ach.authorize_livewire'; + } + + return 'gateways.braintree.ach.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->braintree; + $data['currency'] = $this->braintree->client->getCurrencyCode(); + $data['payment_method_id'] = GatewayType::BANK_TRANSFER; + $data['amount'] = $this->braintree->payment_hash->data->amount_with_fee; + $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); + $data['payment_hash'] = $this->braintree->payment_hash->hash; + + if (count($data['tokens']) === 0) { + $data['authorize_then_redirect'] = true; + } + + return $data; + } } diff --git a/app/PaymentDrivers/Braintree/CreditCard.php b/app/PaymentDrivers/Braintree/CreditCard.php index 3ab0fdbe3de7..197f863ab3b3 100644 --- a/app/PaymentDrivers/Braintree/CreditCard.php +++ b/app/PaymentDrivers/Braintree/CreditCard.php @@ -21,8 +21,9 @@ use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\BraintreePaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; -class CreditCard +class CreditCard implements LivewireMethodInterface { /** * @var BraintreePaymentDriver @@ -76,17 +77,7 @@ class CreditCard public function paymentView(array $data) { - $data['gateway'] = $this->braintree; - $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); - $data['threeds'] = $this->threeDParameters($data); - $data['threeds_enable'] = $this->braintree->company_gateway->getConfigField('threeds') ? "true" : "false"; - - if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) { - /** https://developer.paypal.com/braintree/docs/reference/request/client-token/generate#merchant_account_id */ - $data['client_token'] = $this->braintree->gateway->clientToken()->generate([ //@phpstan-ignore-line - 'merchantAccountId' => $this->braintree->company_gateway->getConfigField('merchantAccountId'), - ]); - } + $data = $this->paymentData($data); return render('gateways.braintree.credit_card.pay', $data); } @@ -278,4 +269,32 @@ class CreditCard return $this->braintree->processInternallyFailedPayment($this->braintree, $e); } } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.braintree.credit_card.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->braintree; + $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); + $data['threeds'] = $this->threeDParameters($data); + $data['threeds_enable'] = $this->braintree->company_gateway->getConfigField('threeds') ? "true" : "false"; + + if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) { + /** https://developer.paypal.com/braintree/docs/reference/request/client-token/generate#merchant_account_id */ + $data['client_token'] = $this->braintree->gateway->clientToken()->generate([ // @phpstan-ignore-line + 'merchantAccountId' => $this->braintree->company_gateway->getConfigField('merchantAccountId'), + ]); + } + + return $data; + } } diff --git a/app/PaymentDrivers/Braintree/PayPal.php b/app/PaymentDrivers/Braintree/PayPal.php index 6de57c1f15e9..2ad38515484d 100644 --- a/app/PaymentDrivers/Braintree/PayPal.php +++ b/app/PaymentDrivers/Braintree/PayPal.php @@ -10,8 +10,9 @@ use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\BraintreePaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; -class PayPal +class PayPal implements LivewireMethodInterface { /** * @var BraintreePaymentDriver @@ -45,8 +46,7 @@ class PayPal */ public function paymentView(array $data) { - $data['gateway'] = $this->braintree; - $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); + $data = $this->paymentData($data); return render('gateways.braintree.paypal.pay', $data); } @@ -188,4 +188,23 @@ class PayPal return $this->braintree->processInternallyFailedPayment($this->braintree, $e); } } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.braintree.paypal.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->braintree; + $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); + + return $data; + } } diff --git a/app/PaymentDrivers/CheckoutCom/CreditCard.php b/app/PaymentDrivers/CheckoutCom/CreditCard.php index c19acab18e6d..b41bb578918a 100644 --- a/app/PaymentDrivers/CheckoutCom/CreditCard.php +++ b/app/PaymentDrivers/CheckoutCom/CreditCard.php @@ -19,6 +19,7 @@ use App\Models\ClientGatewayToken; use App\Models\GatewayType; use App\Models\SystemLog; use App\PaymentDrivers\CheckoutComPaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\Utils\Traits\MakesHash; use Checkout\CheckoutApiException; @@ -32,7 +33,7 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\Request; use Illuminate\View\View; -class CreditCard implements MethodInterface +class CreditCard implements MethodInterface, LivewireMethodInterface { use Utilities; use MakesHash; @@ -140,7 +141,7 @@ class CreditCard implements MethodInterface } } - public function paymentView($data) + public function paymentData(array $data): array { $data['gateway'] = $this->checkout; $data['company_gateway'] = $this->checkout->company_gateway; @@ -150,9 +151,25 @@ class CreditCard implements MethodInterface $data['raw_value'] = $data['total']['amount_with_fee']; $data['customer_email'] = $this->checkout->client->present()->email(); + return $data; + } + + public function paymentView($data, $livewire = false) + { + $data = $this->paymentData($data); + + if ($livewire) { + return render('gateways.checkout.credit_card.pay_livewire', $data); + } + return render('gateways.checkout.credit_card.pay', $data); } + public function livewirePaymentView(array $data): string + { + return 'gateways.checkout.credit_card.pay_livewire'; + } + public function paymentResponse(PaymentResponseRequest $request) { $state = [ diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index 12dd66244f8d..1611b898cfaa 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -619,4 +619,9 @@ class CheckoutComPaymentDriver extends BaseDriver }); } + + public function livewirePaymentView(array $data): string + { + return $this->payment_method->livewirePaymentView($data); + } } diff --git a/app/PaymentDrivers/Eway/CreditCard.php b/app/PaymentDrivers/Eway/CreditCard.php index e7f97124566d..a3fbb56d5fd3 100644 --- a/app/PaymentDrivers/Eway/CreditCard.php +++ b/app/PaymentDrivers/Eway/CreditCard.php @@ -17,10 +17,11 @@ use App\Jobs\Util\SystemLogger; use App\Models\GatewayType; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\EwayPaymentDriver; use App\Utils\Traits\MakesHash; -class CreditCard +class CreditCard implements LivewireMethodInterface { use MakesHash; @@ -102,11 +103,18 @@ class CreditCard return $token; } - public function paymentView($data) + public function paymentData(array $data): array { $data['gateway'] = $this->eway_driver; $data['public_api_key'] = $this->eway_driver->company_gateway->getConfigField('publicApiKey'); + return $data; + } + + public function paymentView($data) + { + $data = $this->paymentData($data); + return render('gateways.eway.pay', $data); } @@ -276,4 +284,8 @@ class CreditCard return $payment; } + public function livewirePaymentView(array $data): string + { + return 'gateways.eway.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Forte/ACH.php b/app/PaymentDrivers/Forte/ACH.php index 8ea313e77200..1fa4c4a2ab07 100644 --- a/app/PaymentDrivers/Forte/ACH.php +++ b/app/PaymentDrivers/Forte/ACH.php @@ -19,11 +19,12 @@ use App\Models\Payment; use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\FortePaymentDriver; use App\Utils\Traits\MakesHash; use Illuminate\Support\Facades\Validator; -class ACH +class ACH implements LivewireMethodInterface { use MakesHash; @@ -79,10 +80,8 @@ class ACH public function paymentView(array $data) { - $this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data); - $this->forte->payment_hash->save(); + $data = $this->paymentData($data); - $data['gateway'] = $this->forte; return render('gateways.forte.ach.pay', $data); } @@ -147,9 +146,14 @@ class ACH $this->forte->client, $this->forte->client->company, ); + $error = Validator::make([], []); + $error->getMessageBag()->add('gateway_error', $response->response->response_desc); - return redirect('client/invoices')->withErrors($error); + + return redirect()->route('client.invoice.show', ['invoice' => $payment_hash->fee_invoice->hashed_id])->withErrors($error); + + // return response()->redirect('client/invoices')->withErrors($error); } SystemLogger::dispatch( @@ -175,4 +179,25 @@ class ACH return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.forte.ach.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data); + $this->forte->payment_hash->save(); + + $data['gateway'] = $this->forte; + + return $data; + } } diff --git a/app/PaymentDrivers/Forte/CreditCard.php b/app/PaymentDrivers/Forte/CreditCard.php index 04942e046b74..cc4256171f27 100644 --- a/app/PaymentDrivers/Forte/CreditCard.php +++ b/app/PaymentDrivers/Forte/CreditCard.php @@ -20,11 +20,12 @@ use App\Models\Payment; use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\FortePaymentDriver; use App\Utils\Traits\MakesHash; use Illuminate\Support\Facades\Validator; -class CreditCard +class CreditCard implements LivewireMethodInterface { use MakesHash; @@ -157,10 +158,8 @@ class CreditCard public function paymentView(array $data) { - $this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data); - $this->forte->payment_hash->save(); - - $data['gateway'] = $this->forte; + $data = $this->paymentData($data); + return render('gateways.forte.credit_card.pay', $data); } @@ -259,7 +258,10 @@ class CreditCard ); $error = Validator::make([], []); $error->getMessageBag()->add('gateway_error', $response->response->response_desc); - return redirect('client/invoices')->withErrors($error); + + return redirect()->route('client.invoice.show', ['invoice' => $payment_hash->fee_invoice->hashed_id])->withErrors($error); + + // return redirect('client/invoices')->withErrors($error); } SystemLogger::dispatch( @@ -287,4 +289,25 @@ class CreditCard return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.forte.credit_card.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data); + $this->forte->payment_hash->save(); + + $data['gateway'] = $this->forte; + + return $data; + } } diff --git a/app/PaymentDrivers/GoCardless/ACH.php b/app/PaymentDrivers/GoCardless/ACH.php index e52e8ce54101..9a8d051373e8 100644 --- a/app/PaymentDrivers/GoCardless/ACH.php +++ b/app/PaymentDrivers/GoCardless/ACH.php @@ -20,6 +20,7 @@ use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\GoCardlessPaymentDriver; use App\Utils\Traits\MakesHash; @@ -31,7 +32,7 @@ use Illuminate\Routing\Redirector; use Illuminate\View\View; //@deprecated -class ACH implements MethodInterface +class ACH implements MethodInterface, LivewireMethodInterface { use MakesHash; @@ -146,9 +147,7 @@ class ACH implements MethodInterface */ public function paymentView(array $data): View { - $data['gateway'] = $this->go_cardless; - $data['amount'] = $this->go_cardless->convertToGoCardlessAmount($data['total']['amount_with_fee'], $this->go_cardless->client->currency()->precision); - $data['currency'] = $this->go_cardless->client->getCurrencyCode(); + $data = $this->paymentData($data); return render('gateways.gocardless.ach.pay', $data); } @@ -257,4 +256,23 @@ class ACH implements MethodInterface throw new PaymentFailed('Failed to process the payment.', 500); } + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.gocardless.ach.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->go_cardless; + $data['amount'] = $this->go_cardless->convertToGoCardlessAmount($data['total']['amount_with_fee'], $this->go_cardless->client->currency()->precision); + $data['currency'] = $this->go_cardless->client->getCurrencyCode(); + + return $data; + } } diff --git a/app/PaymentDrivers/GoCardless/DirectDebit.php b/app/PaymentDrivers/GoCardless/DirectDebit.php index ab67f2862263..515aef5451e0 100644 --- a/app/PaymentDrivers/GoCardless/DirectDebit.php +++ b/app/PaymentDrivers/GoCardless/DirectDebit.php @@ -13,23 +13,25 @@ namespace App\PaymentDrivers\GoCardless; use App\Exceptions\PaymentFailed; +use App\Http\Controllers\ClientPortal\InvoiceController; +use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Jobs\Mail\PaymentFailureMailer; use App\Jobs\Util\SystemLogger; use App\Models\GatewayType; use App\Models\Invoice; use App\Models\Payment; +use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\GoCardlessPaymentDriver; use App\Utils\Traits\MakesHash; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Routing\Redirector; -use Illuminate\View\View; -class DirectDebit implements MethodInterface +class DirectDebit implements MethodInterface, LivewireMethodInterface { use MakesHash; @@ -93,6 +95,8 @@ class DirectDebit implements MethodInterface 'method' => GatewayType::DIRECT_DEBIT, 'session_token' => $session_token, 'billing_request' => $response->id, + 'authorize_then_redirect' => true, + 'payment_hash' => $this->go_cardless->payment_hash?->hash, ]), "exit_uri" => $exit_uri, "links" => [ @@ -142,7 +146,6 @@ class DirectDebit implements MethodInterface */ public function authorizeResponse(Request $request) { - try { $billing_request = $this->go_cardless->gateway->billingRequests()->get($request->billing_request); @@ -165,6 +168,22 @@ class DirectDebit implements MethodInterface nlog($mandate); + if ($request->has('authorize_then_redirect') && $request->payment_hash !== null) { + $this->go_cardless->payment_hash = PaymentHash::where('hash', $request->payment_hash)->firstOrFail(); + + $data = [ + 'invoices' => collect($this->go_cardless->payment_hash->data->invoices)->map(fn ($invoice) => $invoice->invoice_id)->toArray(), + 'action' => 'payment', + ]; + + $request = new ProcessInvoicesInBulkRequest(); + $request->replace($data); + + session()->flash('message', ctrans('texts.payment_method_added')); + + return app(InvoiceController::class)->bulk($request); + } + return redirect()->route('client.payment_methods.show', $payment_method->hashed_id); } catch (\Exception $exception) { @@ -214,13 +233,10 @@ class DirectDebit implements MethodInterface * Payment view for Direct Debit. * * @param array $data - * @return \Illuminate\View\View */ - public function paymentView(array $data): View + public function paymentView(array $data) { - $data['gateway'] = $this->go_cardless; - $data['amount'] = $this->go_cardless->convertToGoCardlessAmount($data['total']['amount_with_fee'], $this->go_cardless->client->currency()->precision); - $data['currency'] = $this->go_cardless->client->getCurrencyCode(); + $data = $this->paymentData($data); return render('gateways.gocardless.direct_debit.pay', $data); } @@ -330,4 +346,30 @@ class DirectDebit implements MethodInterface throw new PaymentFailed('Failed to process the payment.', 500); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.gocardless.direct_debit.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->go_cardless; + $data['amount'] = $this->go_cardless->convertToGoCardlessAmount($data['total']['amount_with_fee'], $this->go_cardless->client->currency()->precision); + $data['currency'] = $this->go_cardless->client->getCurrencyCode(); + + if (count($data['tokens']) === 0) { + $data['authorize_then_redirect'] = true; + + $this->authorizeView($data); + } + + return $data; + } } diff --git a/app/PaymentDrivers/GoCardless/InstantBankPay.php b/app/PaymentDrivers/GoCardless/InstantBankPay.php index 624f04eebe7c..c286e1c1b0aa 100644 --- a/app/PaymentDrivers/GoCardless/InstantBankPay.php +++ b/app/PaymentDrivers/GoCardless/InstantBankPay.php @@ -9,13 +9,14 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\GoCardlessPaymentDriver; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -class InstantBankPay implements MethodInterface +class InstantBankPay implements MethodInterface, LivewireMethodInterface { protected GoCardlessPaymentDriver $go_cardless; @@ -94,12 +95,15 @@ class InstantBankPay implements MethodInterface public function paymentResponse($request) { + $this->go_cardless->setPaymentHash( $request->getPaymentHash() ); $this->go_cardless->init(); + nlog($request->all()); + try { $billing_request = $this->go_cardless->gateway->billingRequests()->get( $this->go_cardless->payment_hash->data->billing_request @@ -116,12 +120,13 @@ class InstantBankPay implements MethodInterface return $this->processSuccessfulPayment($payment); } - if ($billing_request->status === 'submitted') { + if (in_array($billing_request->status, ['fulfilling', 'submitted'])) { return $this->processPendingPayment($payment); } - return $this->processUnsuccessfulPayment($payment); + $this->processUnsuccessfulPayment($payment); } catch (\Exception $exception) { + throw new PaymentFailed( $exception->getMessage(), $exception->getCode() @@ -157,7 +162,7 @@ class InstantBankPay implements MethodInterface $this->go_cardless->client->company, ); - return redirect()->route('client.payments.show', ['payment' => $this->go_cardless->encodePrimaryKey($payment->id)]); + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } @@ -197,9 +202,8 @@ class InstantBankPay implements MethodInterface * Process unsuccessful payments for Direct Debit. * * @param ResourcesPayment $payment - * @return never */ - public function processUnsuccessfulPayment(\GoCardlessPro\Resources\Payment $payment) + public function processUnsuccessfulPayment(\GoCardlessPro\Resources\Payment $payment): void { PaymentFailureMailer::dispatch($this->go_cardless->client, $payment->status, $this->go_cardless->client->company, $this->go_cardless->payment_hash->data->amount_with_fee); @@ -224,4 +228,24 @@ class InstantBankPay implements MethodInterface $this->go_cardless->client->company, ); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + // not supported, this is offsite payment method. + + return ''; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->paymentView($data); + + return $data; + } } diff --git a/app/PaymentDrivers/GoCardless/SEPA.php b/app/PaymentDrivers/GoCardless/SEPA.php index d0f81228ceff..62cf1ebe1113 100644 --- a/app/PaymentDrivers/GoCardless/SEPA.php +++ b/app/PaymentDrivers/GoCardless/SEPA.php @@ -13,13 +13,17 @@ namespace App\PaymentDrivers\GoCardless; use App\Exceptions\PaymentFailed; +use App\Http\Controllers\ClientPortal\InvoiceController; +use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Jobs\Util\SystemLogger; use App\Models\GatewayType; use App\Models\Invoice; use App\Models\Payment; +use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\GoCardlessPaymentDriver; use App\Utils\Traits\MakesHash; @@ -29,7 +33,7 @@ use Illuminate\Http\Request; use Illuminate\Routing\Redirector; use Illuminate\View\View; -class SEPA implements MethodInterface +class SEPA implements MethodInterface, LivewireMethodInterface { use MakesHash; @@ -60,6 +64,8 @@ class SEPA implements MethodInterface 'success_redirect_url' => route('client.payment_methods.confirm', [ 'method' => GatewayType::SEPA, 'session_token' => $session_token, + 'authorize_then_redirect' => true, + 'payment_hash' => $this->go_cardless->payment_hash?->hash, ]), 'prefilled_customer' => [ 'given_name' => auth()->guard('contact')->user()->client->present()->first_name(), @@ -131,6 +137,22 @@ class SEPA implements MethodInterface $payment_method = $this->go_cardless->storeGatewayToken($data, ['gateway_customer_reference' => $redirect_flow->links->customer]); + if ($request->has('authorize_then_redirect') && $request->payment_hash !== null) { + $this->go_cardless->payment_hash = PaymentHash::where('hash', $request->payment_hash)->firstOrFail(); + + $data = [ + 'invoices' => collect($this->go_cardless->payment_hash->data->invoices)->map(fn ($invoice) => $invoice->invoice_id)->toArray(), + 'action' => 'payment', + ]; + + $request = new ProcessInvoicesInBulkRequest(); + $request->replace($data); + + session()->flash('message', ctrans('texts.payment_method_added')); + + return app(InvoiceController::class)->bulk($request); + } + return redirect()->route('client.payment_methods.show', $payment_method->hashed_id); } catch (\Exception $exception) { return $this->processUnsuccessfulAuthorization($exception); @@ -145,9 +167,7 @@ class SEPA implements MethodInterface */ public function paymentView(array $data): View { - $data['gateway'] = $this->go_cardless; - $data['amount'] = $this->go_cardless->convertToGoCardlessAmount($data['total']['amount_with_fee'], $this->go_cardless->client->currency()->precision); - $data['currency'] = $this->go_cardless->client->getCurrencyCode(); + $data = $this->paymentData($data); return render('gateways.gocardless.sepa.pay', $data); } @@ -257,4 +277,30 @@ class SEPA implements MethodInterface throw new PaymentFailed('Failed to process the payment.', 500); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.gocardless.sepa.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->go_cardless; + $data['amount'] = $this->go_cardless->convertToGoCardlessAmount($data['total']['amount_with_fee'], $this->go_cardless->client->currency()->precision); + $data['currency'] = $this->go_cardless->client->getCurrencyCode(); + + if (count($data['tokens']) === 0) { + $data['authorize_then_redirect'] = true; + + $this->authorizeView($data); + } + + return $data; + } } diff --git a/app/PaymentDrivers/GoCardlessPaymentDriver.php b/app/PaymentDrivers/GoCardlessPaymentDriver.php index 67dce0892ed8..3e88487e5c91 100644 --- a/app/PaymentDrivers/GoCardlessPaymentDriver.php +++ b/app/PaymentDrivers/GoCardlessPaymentDriver.php @@ -256,7 +256,7 @@ class GoCardlessPaymentDriver extends BaseDriver $this->init(); nlog('GoCardless Event'); - + nlog($request->all()); if (! $request->has('events')) { nlog('No GoCardless events to process in response?'); @@ -266,7 +266,10 @@ class GoCardlessPaymentDriver extends BaseDriver sleep(1); foreach ($request->events as $event) { - if ($event['action'] === 'confirmed' || $event['action'] === 'paid_out') { + if ( + ($event['resource_type'] == 'payments' && $event['action'] == 'confirmed') || + $event['action'] === 'paid_out') + { nlog('Searching for transaction reference'); $payment = Payment::query() diff --git a/app/PaymentDrivers/Mollie/Bancontact.php b/app/PaymentDrivers/Mollie/Bancontact.php index 3df988bbb2ef..3a19ac358fb7 100644 --- a/app/PaymentDrivers/Mollie/Bancontact.php +++ b/app/PaymentDrivers/Mollie/Bancontact.php @@ -19,13 +19,14 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\MolliePaymentDriver; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; -class Bancontact implements MethodInterface +class Bancontact implements MethodInterface, LivewireMethodInterface { protected MolliePaymentDriver $mollie; @@ -209,4 +210,24 @@ class Bancontact implements MethodInterface { return $this->processSuccessfulPayment($payment, 'open'); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + // Doesn't support, it's offsite payment method. + + return ''; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->paymentView($data); + + return $data; + } } diff --git a/app/PaymentDrivers/Mollie/BankTransfer.php b/app/PaymentDrivers/Mollie/BankTransfer.php index 7c5872714c06..642d4bfc84c6 100644 --- a/app/PaymentDrivers/Mollie/BankTransfer.php +++ b/app/PaymentDrivers/Mollie/BankTransfer.php @@ -19,6 +19,7 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\MolliePaymentDriver; use Exception; @@ -28,7 +29,7 @@ use Illuminate\Routing\Redirector; use Illuminate\View\View; use Mollie\Api\Resources\Payment as ResourcesPayment; -class BankTransfer implements MethodInterface +class BankTransfer implements MethodInterface, LivewireMethodInterface { protected MolliePaymentDriver $mollie; @@ -206,4 +207,24 @@ class BankTransfer implements MethodInterface { return $this->processSuccessfulPayment($payment, 'open'); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + // Doesn't support, it's offsite payment method. + + return ''; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->paymentView($data); + + return $data; + } } diff --git a/app/PaymentDrivers/Mollie/CreditCard.php b/app/PaymentDrivers/Mollie/CreditCard.php index 627605974532..6e712c8f84b6 100644 --- a/app/PaymentDrivers/Mollie/CreditCard.php +++ b/app/PaymentDrivers/Mollie/CreditCard.php @@ -10,12 +10,13 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\MolliePaymentDriver; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\View\View; -class CreditCard +class CreditCard implements LivewireMethodInterface { /** * @var MolliePaymentDriver @@ -37,7 +38,7 @@ class CreditCard */ public function paymentView(array $data) { - $data['gateway'] = $this->mollie; + $data = $this->paymentData($data); return render('gateways.mollie.credit_card.pay', $data); } @@ -257,4 +258,22 @@ class CreditCard { return redirect()->route('client.payment_methods.index'); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.mollie.credit_card.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->mollie; + + return $data; + } } diff --git a/app/PaymentDrivers/Mollie/IDEAL.php b/app/PaymentDrivers/Mollie/IDEAL.php index 3b19cad34f11..134853a6fe04 100644 --- a/app/PaymentDrivers/Mollie/IDEAL.php +++ b/app/PaymentDrivers/Mollie/IDEAL.php @@ -19,13 +19,14 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\MolliePaymentDriver; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; -class IDEAL implements MethodInterface +class IDEAL implements MethodInterface, LivewireMethodInterface { protected MolliePaymentDriver $mollie; @@ -209,4 +210,24 @@ class IDEAL implements MethodInterface { return $this->processSuccessfulPayment($payment, 'open'); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + // Doesn't support, it's offsite payment method. + + return ''; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->paymentView($data); + + return $data; + } } diff --git a/app/PaymentDrivers/Mollie/KBC.php b/app/PaymentDrivers/Mollie/KBC.php index 00b87934a92d..0811d7567454 100644 --- a/app/PaymentDrivers/Mollie/KBC.php +++ b/app/PaymentDrivers/Mollie/KBC.php @@ -19,13 +19,14 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\MolliePaymentDriver; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; -class KBC implements MethodInterface +class KBC implements MethodInterface, LivewireMethodInterface { protected MolliePaymentDriver $mollie; @@ -193,4 +194,24 @@ class KBC implements MethodInterface return redirect()->route('client.payments.show', ['payment' => $this->mollie->encodePrimaryKey($payment_record->id)]); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + // Doesn't support, it's offsite payment method. + + return ''; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $this->paymentView($data); + + return $data; + } } diff --git a/app/PaymentDrivers/PayFast/CreditCard.php b/app/PaymentDrivers/PayFast/CreditCard.php index d3ca7b87cd10..7b5e2380a054 100644 --- a/app/PaymentDrivers/PayFast/CreditCard.php +++ b/app/PaymentDrivers/PayFast/CreditCard.php @@ -18,12 +18,13 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\PayFastPaymentDriver; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; -class CreditCard +class CreditCard implements LivewireMethodInterface { public $payfast; @@ -158,24 +159,9 @@ class CreditCard public function paymentView($data) { - $payfast_data = [ - 'merchant_id' => $this->payfast->company_gateway->getConfigField('merchantId'), - 'merchant_key' => $this->payfast->company_gateway->getConfigField('merchantKey'), - 'return_url' => route('client.payments.index'), - 'cancel_url' => route('client.payment_methods.index'), - 'notify_url' => $this->payfast->genericWebhookUrl(), - 'm_payment_id' => $data['payment_hash'], - 'amount' => $data['amount_with_fee'], - 'item_name' => 'purchase', - 'item_description' => ctrans('texts.invoices').': '.collect($data['invoices'])->pluck('invoice_number'), - 'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'), - ]; + $data = $this->paymentData($data); - $payfast_data['signature'] = $this->payfast->generateSignature($payfast_data); - $payfast_data['gateway'] = $this->payfast; - $payfast_data['payment_endpoint_url'] = $this->payfast->endpointUrl(); - - return render('gateways.payfast.pay', array_merge($data, $payfast_data)); + return render('gateways.payfast.pay', array_merge($data)); } /* @@ -240,7 +226,7 @@ class CreditCard $payment = $this->payfast->createPayment($payment_record, Payment::STATUS_COMPLETED); - //return redirect()->route('client.payments.show', ['payment' => $this->payfast->encodePrimaryKey($payment->id)]); + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } private function processUnsuccessfulPayment($server_response) @@ -263,4 +249,36 @@ class CreditCard throw new PaymentFailed('Failed to process the payment.', 500); } + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.payfast.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $payfast_data = [ + 'merchant_id' => $this->payfast->company_gateway->getConfigField('merchantId'), + 'merchant_key' => $this->payfast->company_gateway->getConfigField('merchantKey'), + 'return_url' => route('client.invoice.show',['invoice' => $this->payfast->payment_hash->fee_invoice->hashed_id]), //route('client.payments.index'), + 'cancel_url' => route('client.invoice.show',['invoice' => $this->payfast->payment_hash->fee_invoice->hashed_id]), //route('client.payment_methods.index'), + 'notify_url' => $this->payfast->genericWebhookUrl(), + 'm_payment_id' => $data['payment_hash'], + 'amount' => $data['amount_with_fee'], + 'item_name' => 'purchase', + 'item_description' => ctrans('texts.invoices').': '.collect($data['invoices'])->pluck('invoice_number'), + 'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'), + ]; + + $payfast_data['signature'] = $this->payfast->generateSignature($payfast_data); + $payfast_data['gateway'] = $this->payfast; + $payfast_data['payment_endpoint_url'] = $this->payfast->endpointUrl(); + + return array_merge($data, $payfast_data); + } } diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php index a62b24b6be8b..161700a6ce93 100644 --- a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php +++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php @@ -85,30 +85,13 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver */ public function processPaymentView($data) { - - $this->init()->checkPaymentsReceivable(); - - $data['gateway'] = $this; - $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); - $this->payment_hash->save(); - - $data['client_id'] = config('ninja.paypal.client_id'); - $data['token'] = $this->getClientToken(); - $data['order_id'] = $this->createOrder($data); - $data['funding_source'] = $this->paypal_payment_method; - $data['gateway_type_id'] = $this->gateway_type_id; - $data['merchantId'] = $this->company_gateway->getConfigField('merchantId'); - $data['currency'] = $this->client->currency()->code; - $data['guid'] = $this->risk_guid; - $data['identifier'] = "s:INN_".$this->company_gateway->getConfigField('merchantId')."_CHCK"; - $data['pp_client_reference'] = $this->getClientHash(); + $data = $this->processPaymentViewData($data); if($this->gateway_type_id == 29) { return render('gateways.paypal.ppcp.card', $data); } else { return render('gateways.paypal.ppcp.pay', $data); } - } /** @@ -480,7 +463,35 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver } + public function processPaymentViewData(array $data): array + { + $this->init()->checkPaymentsReceivable(); + $data['gateway'] = $this; + $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); + $this->payment_hash->save(); + $data['client_id'] = config('ninja.paypal.client_id'); + $data['token'] = $this->getClientToken(); + $data['order_id'] = $this->createOrder($data); + $data['funding_source'] = $this->paypal_payment_method; + $data['gateway_type_id'] = $this->gateway_type_id; + $data['merchantId'] = $this->company_gateway->getConfigField('merchantId'); + $data['currency'] = $this->client->currency()->code; + $data['guid'] = $this->risk_guid; + $data['identifier'] = "s:INN_".$this->company_gateway->getConfigField('merchantId')."_CHCK"; + $data['pp_client_reference'] = $this->getClientHash(); + $data['invoice_hash'] = $this->payment_hash->fee_invoice->hashed_id; + return $data; + } + + public function livewirePaymentView(array $data): string + { + if ($this->gateway_type_id == 29) { + return 'gateways.paypal.ppcp.card_livewire'; + } + + return 'gateways.paypal.ppcp.pay_livewire'; + } } diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php index dfbf86b75943..f76b1e11f5d6 100644 --- a/app/PaymentDrivers/PayPalRestPaymentDriver.php +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -31,30 +31,13 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver public function processPaymentView($data) { - - $this->init(); - - $data['gateway'] = $this; - - $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); - $this->payment_hash->save(); - - $data['client_id'] = $this->company_gateway->getConfigField('clientId'); - $data['token'] = $this->getClientToken(); - $data['order_id'] = $this->createOrder($data); - $data['funding_source'] = $this->paypal_payment_method; - $data['gateway_type_id'] = $this->gateway_type_id; - $data['currency'] = $this->client->currency()->code; - $data['guid'] = $this->risk_guid; - $data['identifier'] = "s:INN_ACDC_CHCK"; - $data['pp_client_reference'] = $this->getClientHash(); + $data = $this->processPaymentViewData($data); if($this->gateway_type_id == 29) { return render('gateways.paypal.ppcp.card', $data); } else { return render('gateways.paypal.pay', $data); } - } /** @@ -71,8 +54,6 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver $request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']); $response = json_decode($request['gateway_response'], true); - nlog($response); - if($request->has('token') && strlen($request->input('token')) > 2) { return $this->processTokenPayment($request, $response); } @@ -434,4 +415,36 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_PAYPAL, $this->client, $this->client->company); } + + public function processPaymentViewData(array $data): array + { + $this->init(); + + $data['gateway'] = $this; + + $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); + $this->payment_hash->save(); + + $data['client_id'] = $this->company_gateway->getConfigField('clientId'); + $data['token'] = $this->getClientToken(); + $data['order_id'] = $this->createOrder($data); + $data['funding_source'] = $this->paypal_payment_method; + $data['gateway_type_id'] = $this->gateway_type_id; + $data['currency'] = $this->client->currency()->code; + $data['guid'] = $this->risk_guid; + $data['identifier'] = "s:INN_ACDC_CHCK"; + $data['pp_client_reference'] = $this->getClientHash(); + $data['invoice_hash'] = $this->payment_hash->fee_invoice->hashed_id; + + return $data; + } + + public function livewirePaymentView(array $data): string + { + if ($this->gateway_type_id == 29) { + return 'gateways.paypal.ppcp.card_livewire'; + } + + return 'gateways.paypal.pay_livewire'; + } } diff --git a/app/PaymentDrivers/PayTrace/CreditCard.php b/app/PaymentDrivers/PayTrace/CreditCard.php index 2c2672aa62a8..970994d3c323 100644 --- a/app/PaymentDrivers/PayTrace/CreditCard.php +++ b/app/PaymentDrivers/PayTrace/CreditCard.php @@ -18,12 +18,13 @@ use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\PaytracePaymentDriver; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; use Illuminate\Support\Str; -class CreditCard +class CreditCard implements LivewireMethodInterface { use MakesHash; @@ -36,9 +37,8 @@ class CreditCard public function authorizeView($data) { - $data['client_key'] = $this->paytrace->getAuthToken(); - $data['gateway'] = $this->paytrace; - + $data = $this->paymentData($data); + return render('gateways.paytrace.authorize', $data); } @@ -239,5 +239,24 @@ class CreditCard ]; return $this->paytrace->processUnsuccessfulTransaction($data); + } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.paytrace.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['client_key'] = $this->paytrace->getAuthToken(); + $data['gateway'] = $this->paytrace; + + return $data; } } diff --git a/app/PaymentDrivers/PaytracePaymentDriver.php b/app/PaymentDrivers/PaytracePaymentDriver.php index bb0348d0a5dd..e573136041c0 100644 --- a/app/PaymentDrivers/PaytracePaymentDriver.php +++ b/app/PaymentDrivers/PaytracePaymentDriver.php @@ -254,7 +254,20 @@ class PaytracePaymentDriver extends BaseDriver public function getClientRequiredFields(): array { - $fields = parent::getClientRequiredFields(); + + $fields = []; + + if ($this->company_gateway->require_client_name) { + $fields[] = ['name' => 'client_name', 'label' => ctrans('texts.client_name'), 'type' => 'text', 'validation' => 'required']; + } + + $fields[] = ['name' => 'contact_first_name', 'label' => ctrans('texts.first_name'), 'type' => 'text', 'validation' => 'required']; + $fields[] = ['name' => 'contact_last_name', 'label' => ctrans('texts.last_name'), 'type' => 'text', 'validation' => 'required']; + $fields[] = ['name' => 'contact_email', 'label' => ctrans('texts.email'), 'type' => 'text', 'validation' => 'required,email:rfc']; + + if ($this->company_gateway->require_client_phone) { + $fields[] = ['name' => 'client_phone', 'label' => ctrans('texts.client_phone'), 'type' => 'tel', 'validation' => 'required']; + } $fields[] = ['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required']; $fields[] = ['name' => 'client_city', 'label' => ctrans('texts.city'), 'type' => 'text', 'validation' => 'required']; @@ -262,6 +275,35 @@ class PaytracePaymentDriver extends BaseDriver $fields[] = ['name' => 'client_state', 'label' => ctrans('texts.state'), 'type' => 'text', 'validation' => 'required']; $fields[] = ['name' => 'client_country_id', 'label' => ctrans('texts.country'), 'type' => 'text', 'validation' => 'required']; + + if ($this->company_gateway->require_shipping_address) { + $fields[] = ['name' => 'client_shipping_address_line_1', 'label' => ctrans('texts.shipping_address1'), 'type' => 'text', 'validation' => 'required']; + $fields[] = ['name' => 'client_shipping_city', 'label' => ctrans('texts.shipping_city'), 'type' => 'text', 'validation' => 'required']; + $fields[] = ['name' => 'client_shipping_state', 'label' => ctrans('texts.shipping_state'), 'type' => 'text', 'validation' => 'required']; + $fields[] = ['name' => 'client_shipping_postal_code', 'label' => ctrans('texts.shipping_postal_code'), 'type' => 'text', 'validation' => 'required']; + $fields[] = ['name' => 'client_shipping_country_id', 'label' => ctrans('texts.shipping_country'), 'type' => 'text', 'validation' => 'required']; + } + + if ($this->company_gateway->require_custom_value1) { + $fields[] = ['name' => 'client_custom_value1', 'label' => $this->helpers->makeCustomField($this->client->company->custom_fields, 'client1'), 'type' => 'text', 'validation' => 'required']; + } + + if ($this->company_gateway->require_custom_value2) { + $fields[] = ['name' => 'client_custom_value2', 'label' => $this->helpers->makeCustomField($this->client->company->custom_fields, 'client2'), 'type' => 'text', 'validation' => 'required']; + } + + + if ($this->company_gateway->require_custom_value3) { + $fields[] = ['name' => 'client_custom_value3', 'label' => $this->helpers->makeCustomField($this->client->company->custom_fields, 'client3'), 'type' => 'text', 'validation' => 'required']; + } + + if ($this->company_gateway->require_custom_value4) { + $fields[] = ['name' => 'client_custom_value4', 'label' => $this->helpers->makeCustomField($this->client->company->custom_fields, 'client4'), 'type' => 'text', 'validation' => 'required']; + } + + + + return $fields; } diff --git a/app/PaymentDrivers/Razorpay/Hosted.php b/app/PaymentDrivers/Razorpay/Hosted.php index 2a8c35bafc1f..9003349cb77e 100644 --- a/app/PaymentDrivers/Razorpay/Hosted.php +++ b/app/PaymentDrivers/Razorpay/Hosted.php @@ -19,6 +19,7 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\RazorpayPaymentDriver; use Illuminate\Http\RedirectResponse; @@ -26,7 +27,7 @@ use Illuminate\Http\Request; use Illuminate\View\View; use Razorpay\Api\Errors\SignatureVerificationError; -class Hosted implements MethodInterface +class Hosted implements MethodInterface, LivewireMethodInterface { protected RazorpayPaymentDriver $razorpay; @@ -67,23 +68,7 @@ class Hosted implements MethodInterface */ public function paymentView(array $data): View { - $order = $this->razorpay->gateway->order->create([ - 'currency' => $this->razorpay->client->currency()->code, - 'amount' => $this->razorpay->convertToRazorpayAmount((float) $this->razorpay->payment_hash->data->amount_with_fee), - ]); - - $this->razorpay->payment_hash->withData('order_id', $order->id); - $this->razorpay->payment_hash->withData('order_amount', $order->amount); - - $data['gateway'] = $this->razorpay; - - $data['options'] = [ - 'key' => $this->razorpay->company_gateway->getConfigField('apiKey'), - 'amount' => $this->razorpay->convertToRazorpayAmount((float) $this->razorpay->payment_hash->data->amount_with_fee), - 'currency' => $this->razorpay->client->currency()->code, - 'name' => $this->razorpay->company_gateway->company->present()->name(), - 'order_id' => $order->id, - ]; + $data = $this->paymentData($data); return render('gateways.razorpay.hosted.pay', $data); } @@ -174,4 +159,38 @@ class Hosted implements MethodInterface throw new PaymentFailed($exception->getMessage(), $exception->getCode()); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.razorpay.hosted.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $order = $this->razorpay->gateway->order->create([ + 'currency' => $this->razorpay->client->currency()->code, + 'amount' => $this->razorpay->convertToRazorpayAmount((float) $this->razorpay->payment_hash->data->amount_with_fee), + ]); + + $this->razorpay->payment_hash->withData('order_id', $order->id); + $this->razorpay->payment_hash->withData('order_amount', $order->amount); + + $data['gateway'] = $this->razorpay; + + $data['options'] = [ + 'key' => $this->razorpay->company_gateway->getConfigField('apiKey'), + 'amount' => $this->razorpay->convertToRazorpayAmount((float) $this->razorpay->payment_hash->data->amount_with_fee), + 'currency' => $this->razorpay->client->currency()->code, + 'name' => $this->razorpay->company_gateway->company->present()->name(), + 'order_id' => $order->id, + ]; + + return $data; + } } diff --git a/app/PaymentDrivers/Rotessa/PaymentMethod.php b/app/PaymentDrivers/Rotessa/PaymentMethod.php index bb427267d844..8ccf3346685d 100755 --- a/app/PaymentDrivers/Rotessa/PaymentMethod.php +++ b/app/PaymentDrivers/Rotessa/PaymentMethod.php @@ -12,7 +12,10 @@ namespace App\PaymentDrivers\Rotessa; +use App\Http\Controllers\ClientPortal\InvoiceController; +use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest; use App\Models\Payment; +use App\Models\PaymentHash; use App\Models\SystemLog; use App\PaymentDrivers\Common\LivewireMethodInterface; use Illuminate\View\View; @@ -112,8 +115,23 @@ class PaymentMethod implements MethodInterface, LivewireMethodInterface } - return redirect()->route('client.payment_methods.index')->withMessage(ctrans('texts.payment_method_added')); + if ($request->authorize_then_redirect) { + $this->rotessa->payment_hash = PaymentHash::where('hash', $request->payment_hash)->firstOrFail(); + $data = [ + 'invoices' => collect($this->rotessa->payment_hash->data->invoices)->map(fn ($invoice) => $invoice->invoice_id)->toArray(), + 'action' => 'payment', + ]; + + $request = new ProcessInvoicesInBulkRequest(); + $request->replace($data); + + session()->flash('message', ctrans('texts.payment_method_added')); + + return app(InvoiceController::class)->bulk($request); + } + + return redirect()->route('client.payment_methods.index')->withMessage(ctrans('texts.payment_method_added')); } /** @@ -126,6 +144,10 @@ class PaymentMethod implements MethodInterface, LivewireMethodInterface { $data = $this->paymentData($data); + if ($data['authorize_then_redirect']) { + return $this->authorizeView($data); + } + return render('gateways.rotessa.bank_transfer.pay', $data ); } @@ -221,6 +243,10 @@ class PaymentMethod implements MethodInterface, LivewireMethodInterface */ public function livewirePaymentView(array $data): string { + if (array_key_exists('authorize_then_redirect', $data)) { + return 'gateways.rotessa.bank_transfer.authorize_livewire'; + } + return 'gateways.rotessa.bank_transfer.pay_livewire'; } @@ -237,6 +263,27 @@ class PaymentMethod implements MethodInterface, LivewireMethodInterface $data['frequency'] = 'Once'; $data['installments'] = 1; $data['invoice_nums'] = $data['invoices']->pluck('invoice_number')->join(', '); + $data['payment_hash'] = $this->rotessa->payment_hash->hash; + + if (count($data['tokens']) === 0) { + $data['authorize_then_redirect'] = true; + + $data['contact'] = collect($data['client']->contacts->first()->toArray())->merge([ + 'home_phone' => $data['client']->phone, + 'custom_identifier' => $data['client']->number, + 'name' => $data['client']->name, + 'id' => null + ] )->all(); + $data['gateway'] = $this->rotessa; + $data['gateway_type_id'] = GatewayType::ACSS ; + $data['account'] = [ + 'routing_number' => $data['client']->routing_id, + 'country' => $data['client']->country->iso_3166_2 + ]; + $data['address'] = collect($data['client']->toArray())->merge(['country' => $data['client']->country->iso_3166_2 ])->all(); + + $this->authorizeView($data); + } return $data; } diff --git a/app/PaymentDrivers/RotessaPaymentDriver.php b/app/PaymentDrivers/RotessaPaymentDriver.php index 1c4a7cf6b5a5..05d941c4dcea 100644 --- a/app/PaymentDrivers/RotessaPaymentDriver.php +++ b/app/PaymentDrivers/RotessaPaymentDriver.php @@ -278,12 +278,12 @@ class RotessaPaymentDriver extends BaseDriver return $this->company_gateway->getConfigField('testMode') ? 'https://sandbox-api.rotessa.com/v1/' : 'https://api.rotessa.com/v1/'; } - public function processPaymentViewData(array $data) + public function processPaymentViewData(array $data): array { return $this->payment_method->paymentData($data); } - public function livewirePaymentView(array $data) + public function livewirePaymentView(array $data): string { return $this->payment_method->livewirePaymentView($data); } diff --git a/app/PaymentDrivers/Square/CreditCard.php b/app/PaymentDrivers/Square/CreditCard.php index 1075b0a292ad..f95428c04ebb 100644 --- a/app/PaymentDrivers/Square/CreditCard.php +++ b/app/PaymentDrivers/Square/CreditCard.php @@ -21,6 +21,7 @@ use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\SquarePaymentDriver; use App\Utils\Traits\MakesHash; @@ -29,7 +30,7 @@ use Illuminate\Http\Request; use Illuminate\View\View; use Square\Http\ApiResponse; -class CreditCard implements MethodInterface +class CreditCard implements MethodInterface, LivewireMethodInterface { use MakesHash; @@ -64,10 +65,7 @@ class CreditCard implements MethodInterface public function paymentView($data) { - $data['gateway'] = $this->square_driver; - $data['amount'] = $this->square_driver->payment_hash->data->amount_with_fee; - $data['currencyCode'] = $this->square_driver->client->getCurrencyCode(); - $data['square_contact'] = $this->buildClientObject(); + $data = $this->paymentData($data); return render('gateways.square.credit_card.pay', $data); } @@ -238,5 +236,24 @@ class CreditCard implements MethodInterface return false; } + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.square.credit_card.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + $data['gateway'] = $this->square_driver; + $data['amount'] = $this->square_driver->payment_hash->data->amount_with_fee; + $data['currencyCode'] = $this->square_driver->client->getCurrencyCode(); + $data['square_contact'] = $this->buildClientObject(); + return $data; + } } diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php index 2802835bc69e..8a0dd8b6a89b 100644 --- a/app/PaymentDrivers/Stripe/ACH.php +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -24,6 +24,7 @@ use App\Models\Payment; use App\Models\PaymentHash; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; use App\Utils\Traits\MakesHash; use Exception; @@ -35,7 +36,7 @@ use Stripe\Exception\InvalidRequestException; use Stripe\Exception\RateLimitException; use Stripe\PaymentIntent; -class ACH +class ACH implements LivewireMethodInterface { use MakesHash; @@ -199,47 +200,7 @@ class ACH */ public function paymentView(array $data) { - $data['gateway'] = $this->stripe; - $data['currency'] = $this->stripe->client->getCurrencyCode(); - $data['payment_method_id'] = GatewayType::BANK_TRANSFER; - $data['customer'] = $this->stripe->findOrCreateCustomer(); - $data['amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - - $description = $this->stripe->getDescription(false); - - $intent = false; - - if (count($data['tokens']) == 1) { - - $token = $data['tokens'][0]; - - $meta = $token->meta; - - if(isset($meta->state) && $meta->state == 'unauthorized') { - return redirect()->route('client.payment_methods.show', $token->hashed_id); - } - } - - if (count($data['tokens']) == 0) { - $intent = - $this->stripe->createPaymentIntent( - [ - 'amount' => $data['amount'], - 'currency' => $data['currency'], - 'setup_future_usage' => 'off_session', - 'customer' => $data['customer']->id, - 'payment_method_types' => ['us_bank_account'], - 'description' => $description, - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::BANK_TRANSFER, - ], - 'statement_descriptor' => $this->stripe->getStatementDescriptor(), - ] - ); - } - - $data['client_secret'] = $intent ? $intent->client_secret : false; + $data = $this->paymentData($data); return render('gateways.stripe.ach.pay', $data); } @@ -628,4 +589,56 @@ class ACH return $this->stripe->processInternallyFailedPayment($this->stripe, $e); } } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.ach.pay_livewire'; + } + + public function paymentData(array $data): array + { + $data['gateway'] = $this->stripe; + $data['currency'] = $this->stripe->client->getCurrencyCode(); + $data['payment_method_id'] = GatewayType::BANK_TRANSFER; + $data['customer'] = $this->stripe->findOrCreateCustomer(); + $data['amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + + $description = $this->stripe->getDescription(false); + + $intent = false; + + if (count($data['tokens']) == 1) { + + $token = $data['tokens'][0]; + + $meta = $token->meta; + + if(isset($meta->state) && $meta->state == 'unauthorized') { + return redirect()->route('client.payment_methods.show', $token->hashed_id); + } + } + + if (count($data['tokens']) == 0) { + $intent = + $this->stripe->createPaymentIntent( + [ + 'amount' => $data['amount'], + 'currency' => $data['currency'], + 'setup_future_usage' => 'off_session', + 'customer' => $data['customer']->id, + 'payment_method_types' => ['us_bank_account'], + 'description' => $description, + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::BANK_TRANSFER, + ], + 'statement_descriptor' => $this->stripe->getStatementDescriptor(), + ] + ); + } + + $data['client_secret'] = $intent ? $intent->client_secret : false; + + return $data; + } } diff --git a/app/PaymentDrivers/Stripe/ACSS.php b/app/PaymentDrivers/Stripe/ACSS.php index 7ef4fa2dde2b..7e5f12f67e71 100644 --- a/app/PaymentDrivers/Stripe/ACSS.php +++ b/app/PaymentDrivers/Stripe/ACSS.php @@ -12,8 +12,11 @@ namespace App\PaymentDrivers\Stripe; +use App\Http\Controllers\ClientPortal\InvoiceController; +use App\Http\Requests\ClientPortal\Invoices\ProcessInvoicesInBulkRequest; use App\Models\Payment; use App\Models\SystemLog; +use Stripe\PaymentIntent; use App\Models\GatewayType; use App\Models\PaymentHash; use App\Models\PaymentType; @@ -26,10 +29,10 @@ use App\Models\ClientGatewayToken; use Illuminate\Support\Facades\Cache; use App\Jobs\Mail\PaymentFailureMailer; use App\PaymentDrivers\StripePaymentDriver; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; -use Stripe\PaymentIntent; -class ACSS +class ACSS implements LivewireMethodInterface { use MakesHash; @@ -62,7 +65,7 @@ class ACSS 'customer' => $data['customer'], 'payment_method_options' => [ 'acss_debit' => [ - 'currency' => 'cad', + 'currency' => strtolower($this->stripe->client->currency()->code), 'mandate_options' => [ 'payment_schedule' => 'combined', 'interval_description' => 'On any invoice due date', @@ -92,7 +95,7 @@ class ACSS $error = "There was a problem setting up this payment method for future use"; - if(in_array($setup_intent->type, ["validation_error", "invalid_request_error"])) { + if (in_array($setup_intent->type, ["validation_error", "invalid_request_error"])) { $error = "Please provide complete payment details."; } @@ -112,22 +115,24 @@ class ACSS $client_gateway_token = $this->storePaymentMethod($setup_intent->payment_method, $stripe_setup_intent->mandate, $setup_intent->status == 'succeeded' ? 'authorized' : 'unauthorized'); - if($request->has('post_auth_response') && boolval($request->post_auth_response)) { + if ($request->has('post_auth_response') && boolval($request->post_auth_response)) { /** @var array $data */ $data = Cache::pull($request->post_auth_response); - if(!$data) { + if (!$data) { throw new PaymentFailed("There was a problem storing this payment method", 500); } $hash = PaymentHash::with('fee_invoice')->where('hash', $data['payment_hash'])->first(); + $data['tokens'] = [$client_gateway_token]; + $data['one_page_checkout'] = (bool) $request->one_page_checkout; $this->stripe->setPaymentHash($hash); $this->stripe->setClient($hash->fee_invoice->client); $this->stripe->setPaymentMethod(GatewayType::ACSS); - return $this->continuePayment($data); + return $this->paymentView($data); } return redirect()->route('client.payment_methods.show', $client_gateway_token->hashed_id); @@ -161,73 +166,43 @@ class ACSS return $intent; } - /** - * Payment view for ACSS - * - * Determines if any payment tokens are available and if not, generates a mandate - * - * @param array $data - - */ - public function paymentView(array $data) + public function paymentData(array $data): array { - if(count($data['tokens']) == 0) { $hash = Str::random(32); + Cache::put($hash, $data, 3600); + $data['post_auth_response'] = $hash; + $data['needs_mandate_generate'] = true; - return $this->generateMandate($data); + $data['gateway'] = $this->stripe; + $data['company_gateway'] = $this->stripe->company_gateway; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\SetupIntent::create([ + 'usage' => 'off_session', + 'payment_method_types' => ['acss_debit'], + 'customer' => $data['customer'], + 'payment_method_options' => [ + 'acss_debit' => [ + 'currency' => strtolower($this->stripe->client->currency()->code), + 'mandate_options' => [ + 'payment_schedule' => 'combined', + 'interval_description' => 'On any invoice due date', + 'transaction_type' => 'personal', + ], + 'verification_method' => 'instant', + ], + ], + ], $this->stripe->stripe_connect_auth); + + $data['pi_client_secret'] = $intent->client_secret; + + return $data; } - return $this->continuePayment($data); - } - - /** - * Generate a payment Mandate for ACSS - * - * @param array $data - - */ - private function generateMandate(array $data) - { - - $data['gateway'] = $this->stripe; - $data['company_gateway'] = $this->stripe->company_gateway; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\SetupIntent::create([ - 'usage' => 'off_session', - 'payment_method_types' => ['acss_debit'], - 'customer' => $data['customer'], - 'payment_method_options' => [ - 'acss_debit' => [ - 'currency' => 'cad', - 'mandate_options' => [ - 'payment_schedule' => 'combined', - 'interval_description' => 'On any invoice due date', - 'transaction_type' => 'personal', - ], - 'verification_method' => 'instant', - ], - ], - ], $this->stripe->stripe_connect_auth); - - $data['pi_client_secret'] = $intent->client_secret; - - return render('gateways.stripe.acss.authorize', array_merge($data)); - - } - - /** - * Continues the payment flow after a Mandate has been successfully generated - * - * @param array $data - */ - private function continuePayment(array $data) - { - $this->stripe->init(); $data['gateway'] = $this->stripe; @@ -240,6 +215,39 @@ class ACSS $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); $this->stripe->payment_hash->save(); + return $data; + } + + /** + * Payment view for ACSS + * + * Determines if any payment tokens are available and if not, generates a mandate + * + * @param array $data + + */ + public function paymentView(array $data) + { + $data = $this->paymentData($data); + + if (isset($data['one_page_checkout']) && $data['one_page_checkout']) { + $data = [ + 'invoices' => collect($data['invoices'])->map(fn ($invoice) => $invoice['invoice_id'])->toArray(), + 'action' => 'payment', + ]; + + $request = new ProcessInvoicesInBulkRequest(); + $request->replace($data); + + session()->flash('message', ctrans('texts.payment_method_added')); + + return app(InvoiceController::class)->bulk($request); + } + + if (array_key_exists('needs_mandate_generate', $data)) { + return render('gateways.stripe.acss.authorize', array_merge($data)); + } + return render('gateways.stripe.acss.pay', $data); } @@ -267,7 +275,6 @@ class ACSS $gateway_response = json_decode($request->gateway_response); - /** @var \App\Models\ClientGatewayToken $cgt */ $cgt = ClientGatewayToken::find($this->decodePrimaryKey($request->token)); /** @var \Stripe\PaymentIntent $intent */ @@ -400,4 +407,13 @@ class ACSS return $this->stripe->processInternallyFailedPayment($this->stripe, $e); } } + + public function livewirePaymentView(array $data): string + { + if (array_key_exists('needs_mandate_generate', $data)) { + return 'gateways.stripe.acss.authorize_livewire'; + } + + return 'gateways.stripe.acss.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/Alipay.php b/app/PaymentDrivers/Stripe/Alipay.php index 96c0930d0752..7ab38a7d1db4 100644 --- a/app/PaymentDrivers/Stripe/Alipay.php +++ b/app/PaymentDrivers/Stripe/Alipay.php @@ -12,16 +12,19 @@ namespace App\PaymentDrivers\Stripe; -use App\Exceptions\PaymentFailed; -use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; -use App\Jobs\Util\SystemLogger; -use App\Models\GatewayType; use App\Models\Payment; -use App\Models\PaymentType; use App\Models\SystemLog; +use App\Models\GatewayType; +use App\Models\PaymentType; +use App\Jobs\Util\SystemLogger; +use App\Exceptions\PaymentFailed; use App\PaymentDrivers\StripePaymentDriver; +use Stripe\Exception\InvalidRequestException; +use App\PaymentDrivers\Common\LivewireMethodInterface; +use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; +use Throwable; -class Alipay +class Alipay implements LivewireMethodInterface { /** @var StripePaymentDriver */ public $stripe; @@ -33,26 +36,9 @@ class Alipay public function paymentView(array $data) { - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), - 'currency' => $this->stripe->client->currency()->code, - 'payment_method_types' => ['alipay'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::ALIPAY, - ], - ], $this->stripe->stripe_connect_auth); - - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['ci_intent'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency())]); - $this->stripe->payment_hash->save(); - + + $data = $this->paymentData($data); + return render('gateways.stripe.alipay.pay', $data); } @@ -81,8 +67,6 @@ class Alipay $this->stripe->stripe_connect_auth ); - nlog($pi); - if (in_array($pi->status, ['succeeded', 'pending'])) { return $this->processSuccesfulRedirect($pi); } @@ -146,4 +130,46 @@ class Alipay throw new PaymentFailed('Failed to process the payment.', 500); } + + /** + * @inheritDoc + */ + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.alipay.pay_livewire'; + } + + /** + * @inheritDoc + */ + public function paymentData(array $data): array + { + try { + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), + 'currency' => $this->stripe->client->currency()->code, + 'payment_method_types' => ['alipay'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::ALIPAY, + ], + ], $this->stripe->stripe_connect_auth); + } + catch(\Throwable $e){ + + throw new PaymentFailed($e->getMessage(), $e->getCode()); + + } + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['ci_intent'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency())]); + $this->stripe->payment_hash->save(); + + return $data; + } } diff --git a/app/PaymentDrivers/Stripe/BACS.php b/app/PaymentDrivers/Stripe/BACS.php index 84ce704a8990..a028a311a4af 100644 --- a/app/PaymentDrivers/Stripe/BACS.php +++ b/app/PaymentDrivers/Stripe/BACS.php @@ -20,12 +20,13 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer; use App\PaymentDrivers\StripePaymentDriver; use App\Utils\Number; use Stripe\Checkout\Session; -class BACS +class BACS implements LivewireMethodInterface { public $stripe; @@ -69,9 +70,7 @@ class BACS } public function paymentView(array $data) { - $data['gateway'] = $this->stripe; - $data['amount'] = $data['total']['amount_with_fee']; - $data['payment_hash'] = $this->stripe->payment_hash->hash; + $data = $this->paymentData($data); return render('gateways.stripe.bacs.pay', $data); } @@ -187,4 +186,18 @@ class BACS return $this->stripe->processInternallyFailedPayment($this->stripe, $e); } } + + public function paymentData(array $data): array + { + $data['gateway'] = $this->stripe; + $data['amount'] = $data['total']['amount_with_fee']; + $data['payment_hash'] = $this->stripe->payment_hash->hash; + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.bacs.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/BECS.php b/app/PaymentDrivers/Stripe/BECS.php index 271e6170f481..2640797770ec 100644 --- a/app/PaymentDrivers/Stripe/BECS.php +++ b/app/PaymentDrivers/Stripe/BECS.php @@ -19,9 +19,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class BECS +class BECS implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -39,33 +40,7 @@ class BECS public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['payment_method_id'] = GatewayType::BECS; - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - $data['payment_hash'] = $this->stripe->payment_hash->hash; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => $this->stripe->client->currency()->code, - 'payment_method_types' => ['au_becs_debit'], - 'setup_future_usage' => 'off_session', - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::BECS, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.becs.pay', $data); } @@ -161,4 +136,42 @@ class BECS return $this->stripe->processInternallyFailedPayment($this->stripe, $e); } } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['payment_method_id'] = GatewayType::BECS; + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + $data['payment_hash'] = $this->stripe->payment_hash->hash; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => $this->stripe->client->currency()->code, + 'payment_method_types' => ['au_becs_debit'], + 'setup_future_usage' => 'off_session', + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::BECS, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.becs.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/Bancontact.php b/app/PaymentDrivers/Stripe/Bancontact.php index a4b0d61fd095..e776377c93a1 100644 --- a/app/PaymentDrivers/Stripe/Bancontact.php +++ b/app/PaymentDrivers/Stripe/Bancontact.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class Bancontact +class Bancontact implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,32 +38,8 @@ class Bancontact public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['bancontact'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::BANCONTACT, - ], - - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); + $data = $this->paymentData($data); return render('gateways.stripe.bancontact.pay', $data); } @@ -93,8 +70,8 @@ class Bancontact /* @todo: https://github.com/invoiceninja/invoiceninja/pull/3789/files#r436175798 */ //catch duplicate submissions. - if (Payment::where('transaction_reference', $payment_intent)->exists()) { - return redirect()->route('client.payments.index'); + if ($payment = Payment::query()->where('transaction_reference', $payment_intent)->first()) { + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } $this->stripe->init(); @@ -107,7 +84,7 @@ class Bancontact 'gateway_type_id' => GatewayType::BANCONTACT, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -118,7 +95,8 @@ class Bancontact $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + +return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -143,4 +121,41 @@ class Bancontact throw new PaymentFailed('Failed to process the payment.', 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['bancontact'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::BANCONTACT, + ], + + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.bancontact.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/BankTransfer.php b/app/PaymentDrivers/Stripe/BankTransfer.php index 4f7a1bac4392..f8d0e26b88ba 100644 --- a/app/PaymentDrivers/Stripe/BankTransfer.php +++ b/app/PaymentDrivers/Stripe/BankTransfer.php @@ -19,12 +19,13 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; use App\Utils\Number; use App\Utils\Traits\MakesHash; use Stripe\PaymentIntent; -class BankTransfer +class BankTransfer implements LivewireMethodInterface { use MakesHash; @@ -38,37 +39,8 @@ class BankTransfer public function paymentView(array $data) { - $this->stripe->init(); - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), - 'currency' => $this->stripe->client->currency()->code, - 'customer' => $this->stripe->findOrCreateCustomer()->id, - 'description' => $this->stripe->getDescription(false), - 'payment_method_types' => ['customer_balance'], - 'payment_method_data' => [ - 'type' => 'customer_balance', - ], - 'payment_method_options' => [ - 'customer_balance' => [ - 'funding_type' => 'bank_transfer', - 'bank_transfer' => $this->resolveBankType() - ], - ], - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::DIRECT_DEBIT, - ], - ], $this->stripe->stripe_connect_auth); - - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency())]); - $this->stripe->payment_hash->save(); - - $data = []; - $data['return_url'] = $this->buildReturnUrl(); - $data['gateway'] = $this->stripe; - $data['client_secret'] = $intent ? $intent->client_secret : false; + $data = $this->paymentData($data); + $data = $this->paymentData($data); return render('gateways.stripe.bank_transfer.pay', $data); } @@ -317,4 +289,46 @@ class BankTransfer throw new PaymentFailed('Failed to process the payment.', 500); } -} + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), + 'currency' => $this->stripe->client->currency()->code, + 'customer' => $this->stripe->findOrCreateCustomer()->id, + 'description' => $this->stripe->getDescription(false), + 'payment_method_types' => ['customer_balance'], + 'payment_method_data' => [ + 'type' => 'customer_balance', + ], + 'payment_method_options' => [ + 'customer_balance' => [ + 'funding_type' => 'bank_transfer', + 'bank_transfer' => $this->resolveBankType() + ], + ], + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::DIRECT_DEBIT, + ], + ], $this->stripe->stripe_connect_auth); + + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency())]); + $this->stripe->payment_hash->save(); + + $data = []; + $data['return_url'] = $this->buildReturnUrl(); + $data['gateway'] = $this->stripe; + $data['client_secret'] = $intent ? $intent->client_secret : false; + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.bank_transfer.pay_livewire'; + } +} \ No newline at end of file diff --git a/app/PaymentDrivers/Stripe/BrowserPay.php b/app/PaymentDrivers/Stripe/BrowserPay.php index 09d71fcdb17c..126afec809c8 100644 --- a/app/PaymentDrivers/Stripe/BrowserPay.php +++ b/app/PaymentDrivers/Stripe/BrowserPay.php @@ -19,6 +19,7 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Common\MethodInterface; use App\PaymentDrivers\StripePaymentDriver; use App\Utils\Ninja; @@ -29,7 +30,7 @@ use Stripe\ApplePayDomain; use Stripe\Exception\ApiErrorException; use Stripe\PaymentIntent; -class BrowserPay implements MethodInterface +class BrowserPay implements MethodInterface, LivewireMethodInterface { protected StripePaymentDriver $stripe; @@ -64,7 +65,8 @@ class BrowserPay implements MethodInterface return redirect()->route('client.payment_methods.index'); } - public function paymentView(array $data): View + + public function paymentData(array $data): array { $payment_intent_data = [ 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), @@ -93,6 +95,13 @@ class BrowserPay implements MethodInterface 'requestPayerEmail' => true, ]; + return $data; + } + + public function paymentView(array $data): View + { + $data = $this->paymentData($data); + return render('gateways.stripe.browser_pay.pay', $data); } @@ -153,7 +162,7 @@ class BrowserPay implements MethodInterface $this->stripe->client->company, ); - return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); + return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]); } /** @@ -231,4 +240,9 @@ class BrowserPay implements MethodInterface return str_replace(['https://', '/public'], '', $domain); } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.browser_pay.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index 6712a160a524..524542d9a8a4 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -50,7 +50,7 @@ class Charge { if ($cgt->gateway_type_id == GatewayType::BANK_TRANSFER) { return (new ACH($this->stripe))->tokenBilling($cgt, $payment_hash); - } elseif($cgt->gateway_type_id == GatewayType::ACSS) { + } elseif ($cgt->gateway_type_id == GatewayType::ACSS) { return (new ACSS($this->stripe))->tokenBilling($cgt, $payment_hash); } @@ -82,6 +82,9 @@ class Charge if ($cgt->gateway_type_id == GatewayType::BACS) { $data['payment_method_types'] = ['bacs_debit']; } + if ($cgt->gateway_type_id == GatewayType::CREDIT_CARD) { + $data['payment_method_types'] = ["card","link"]; + } /* Should improve token billing with client not present */ if (!auth()->guard('contact')->check()) { diff --git a/app/PaymentDrivers/Stripe/CreditCard.php b/app/PaymentDrivers/Stripe/CreditCard.php index eab1a98c3262..bfaa366f6cce 100644 --- a/app/PaymentDrivers/Stripe/CreditCard.php +++ b/app/PaymentDrivers/Stripe/CreditCard.php @@ -19,12 +19,13 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer; use App\PaymentDrivers\StripePaymentDriver; use Stripe\PaymentIntent; use Stripe\PaymentMethod; -class CreditCard +class CreditCard implements LivewireMethodInterface { public $stripe; @@ -57,7 +58,7 @@ class CreditCard return redirect()->route('client.payment_methods.index'); } - public function paymentView(array $data) + public function paymentData(array $data): array { $description = $this->stripe->getDescription(false); @@ -77,9 +78,21 @@ class CreditCard $data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data); $data['gateway'] = $this->stripe; + return $data; + } + + public function paymentView(array $data) + { + $data = $this->paymentData($data); + return render('gateways.stripe.credit_card.pay', $data); } + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.credit_card.pay_livewire'; + } + public function paymentResponse(PaymentResponseRequest $request) { $this->stripe->init(); diff --git a/app/PaymentDrivers/Stripe/EPS.php b/app/PaymentDrivers/Stripe/EPS.php index 8f1456fcebb0..fc70a6079395 100644 --- a/app/PaymentDrivers/Stripe/EPS.php +++ b/app/PaymentDrivers/Stripe/EPS.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class EPS +class EPS implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,31 +38,7 @@ class EPS public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['eps'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::EPS, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.eps.pay', $data); } @@ -94,8 +71,9 @@ class EPS $this->stripe->init(); //catch duplicate submissions. - if (Payment::where('transaction_reference', $payment_intent)->exists()) { - return redirect()->route('client.payments.index'); + if ($payment = Payment::query()->where('transaction_reference', $payment_intent)->first()) { + + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } $data = [ @@ -106,7 +84,7 @@ class EPS 'gateway_type_id' => GatewayType::EPS, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -117,7 +95,8 @@ class EPS $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + +return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -142,4 +121,40 @@ class EPS throw new PaymentFailed('Failed to process the payment.', 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['eps'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::EPS, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.eps.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/FPX.php b/app/PaymentDrivers/Stripe/FPX.php index e97b25562c13..0922b8c77d75 100644 --- a/app/PaymentDrivers/Stripe/FPX.php +++ b/app/PaymentDrivers/Stripe/FPX.php @@ -19,9 +19,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class FPX +class FPX implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -38,31 +39,7 @@ class FPX public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => $this->stripe->client->getCurrencyCode(), - 'payment_method_types' => ['fpx'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::FPX, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.fpx.pay', $data); } @@ -102,7 +79,7 @@ class FPX 'gateway_type_id' => GatewayType::FPX, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -112,8 +89,9 @@ class FPX $this->stripe->client, $this->stripe->client->company, ); + + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); - return redirect()->route('client.payments.index'); } public function processUnsuccessfulPayment() @@ -143,4 +121,40 @@ class FPX throw new PaymentFailed('Failed to process the payment.', 400); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => $this->stripe->client->getCurrencyCode(), + 'payment_method_types' => ['fpx'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::FPX, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.fpx.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/GIROPAY.php b/app/PaymentDrivers/Stripe/GIROPAY.php index cd8828bfb4ea..044c4a4d0705 100644 --- a/app/PaymentDrivers/Stripe/GIROPAY.php +++ b/app/PaymentDrivers/Stripe/GIROPAY.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class GIROPAY +class GIROPAY implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,31 +38,7 @@ class GIROPAY public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['giropay'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::GIROPAY, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.giropay.pay', $data); } @@ -94,8 +71,8 @@ class GIROPAY $this->stripe->init(); //catch duplicate submissions. - if (Payment::where('transaction_reference', $payment_intent)->exists()) { - return redirect()->route('client.payments.index'); + if ($payment = Payment::query()->where('transaction_reference', $payment_intent)->first()) { + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } $data = [ @@ -106,7 +83,7 @@ class GIROPAY 'gateway_type_id' => GatewayType::GIROPAY, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -117,7 +94,7 @@ class GIROPAY $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -142,4 +119,40 @@ class GIROPAY throw new PaymentFailed('Failed to process the payment.', 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['giropay'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::GIROPAY, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.giropay.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/Klarna.php b/app/PaymentDrivers/Stripe/Klarna.php index 09169e4f16a1..9e85bcc0096b 100644 --- a/app/PaymentDrivers/Stripe/Klarna.php +++ b/app/PaymentDrivers/Stripe/Klarna.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class Klarna +class Klarna implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,33 +38,7 @@ class Klarna public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $description = $this->stripe->getDescription(false); - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => $this->stripe->client->getCurrencyCode(), - 'payment_method_types' => ['klarna'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $description, - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::KLARNA, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.klarna.pay', $data); } @@ -94,8 +69,10 @@ class Klarna $this->stripe->init(); //catch duplicate submissions. - if (Payment::where('transaction_reference', $payment_intent)->exists()) { - return redirect()->route('client.payments.index'); + if ($pay_exists = Payment::query()->where('transaction_reference', $payment_intent)->first()) { + + return redirect()->route('client.payments.show', ['payment' => $pay_exists->hashed_id]); + } $data = [ @@ -106,7 +83,7 @@ class Klarna 'gateway_type_id' => GatewayType::KLARNA, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -117,7 +94,7 @@ class Klarna $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -142,4 +119,42 @@ class Klarna throw new PaymentFailed(ctrans('texts.gateway_error'), 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $description = $this->stripe->getDescription(false); + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => $this->stripe->client->getCurrencyCode(), + 'payment_method_types' => ['klarna'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $description, + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::KLARNA, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.klarna.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/PRZELEWY24.php b/app/PaymentDrivers/Stripe/PRZELEWY24.php index ce9bb79405ed..787ba7a76f6a 100644 --- a/app/PaymentDrivers/Stripe/PRZELEWY24.php +++ b/app/PaymentDrivers/Stripe/PRZELEWY24.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class PRZELEWY24 +class PRZELEWY24 implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,31 +38,7 @@ class PRZELEWY24 public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['p24'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::PRZELEWY24, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.przelewy24.pay', $data); } @@ -94,8 +71,8 @@ class PRZELEWY24 $this->stripe->init(); //catch duplicate submissions. - if (Payment::where('transaction_reference', $payment_intent)->exists()) { - return redirect()->route('client.payments.index'); + if ($pay_exists = Payment::query()->where('transaction_reference', $payment_intent)->first()) { + return redirect()->route('client.payments.show', ['payment' => $pay_exists->hashed_id]); } $data = [ @@ -106,7 +83,7 @@ class PRZELEWY24 'gateway_type_id' => GatewayType::PRZELEWY24, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -117,7 +94,8 @@ class PRZELEWY24 $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -142,4 +120,40 @@ class PRZELEWY24 throw new PaymentFailed('Failed to process the payment.', 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['p24'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::PRZELEWY24, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.przelewy24.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/SEPA.php b/app/PaymentDrivers/Stripe/SEPA.php index 79f98e9f6239..c82c1b6f292f 100644 --- a/app/PaymentDrivers/Stripe/SEPA.php +++ b/app/PaymentDrivers/Stripe/SEPA.php @@ -19,9 +19,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class SEPA +class SEPA implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -47,33 +48,7 @@ class SEPA public function paymentView(array $data) { - $data['gateway'] = $this->stripe; - $data['payment_method_id'] = GatewayType::SEPA; - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - $data['payment_hash'] = $this->stripe->payment_hash->hash; - - $intent_data = [ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['sepa_debit'], - 'setup_future_usage' => 'off_session', - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::SEPA, - ], - ]; - - $intent = \Stripe\PaymentIntent::create($intent_data, array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.sepa.pay', $data); } @@ -167,7 +142,7 @@ class SEPA 'company_id' => $this->stripe->client->company_id, ])->first(); - if($token) { + if ($token) { return $token; } @@ -176,4 +151,58 @@ class SEPA return $this->stripe->processInternallyFailedPayment($this->stripe, $e); } } + + public function paymentData(array $data): array + { + $data['gateway'] = $this->stripe; + $data['payment_method_id'] = GatewayType::SEPA; + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + $data['payment_hash'] = $this->stripe->payment_hash->hash; + + + /** if the iban and client country don't match (OR UK IBAN) - need to inject billing details also */ + // $data['billing_details'] = [ + // 'name' => $this->stripe->client->present()->name(), + // 'email' => $this->stripe->client->present()->email(), + // 'address' => [ + // 'line1' => $this->stripe->client->address1 ?? '', + // 'line2' => $this->stripe->client->address2 ?? '', + // 'city' => $this->stripe->client->city ?? '', + // 'state' => $this->stripe->client->state ?? '', + // 'postal_code' => $this->stripe->client->postal_code ?? '', + // 'country' => $this->stripe->client->country->iso_3166_2 ?? 'DE', + // ] + // ]; + + + $intent_data = [ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['sepa_debit'], + 'setup_future_usage' => 'off_session', + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::SEPA, + ], + ]; + + $intent = \Stripe\PaymentIntent::create($intent_data, array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.sepa.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/SOFORT.php b/app/PaymentDrivers/Stripe/SOFORT.php index 4a7faef870c0..14603cc9a1f8 100644 --- a/app/PaymentDrivers/Stripe/SOFORT.php +++ b/app/PaymentDrivers/Stripe/SOFORT.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class SOFORT +class SOFORT implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,31 +38,8 @@ class SOFORT public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['sofort'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::SOFORT, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); + $data = $this->paymentData($data); return render('gateways.stripe.sofort.pay', $data); } @@ -101,7 +79,7 @@ class SOFORT 'gateway_type_id' => GatewayType::SOFORT, ]; - $this->stripe->createPayment($data, Payment::STATUS_PENDING); + $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -112,7 +90,7 @@ class SOFORT $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -137,4 +115,40 @@ class SOFORT throw new PaymentFailed('Failed to process the payment.', 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['sofort'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::SOFORT, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.sofort.pay_livewire'; + } } diff --git a/app/PaymentDrivers/Stripe/iDeal.php b/app/PaymentDrivers/Stripe/iDeal.php index 01b346b7801d..701b07ce8e43 100644 --- a/app/PaymentDrivers/Stripe/iDeal.php +++ b/app/PaymentDrivers/Stripe/iDeal.php @@ -18,9 +18,10 @@ use App\Models\GatewayType; use App\Models\Payment; use App\Models\PaymentType; use App\Models\SystemLog; +use App\PaymentDrivers\Common\LivewireMethodInterface; use App\PaymentDrivers\StripePaymentDriver; -class iDeal +class iDeal implements LivewireMethodInterface { /** @var StripePaymentDriver */ public StripePaymentDriver $stripe; @@ -37,31 +38,7 @@ class iDeal public function paymentView(array $data) { - $this->stripe->init(); - - $data['gateway'] = $this->stripe; - $data['return_url'] = $this->buildReturnUrl(); - $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); - $data['client'] = $this->stripe->client; - $data['customer'] = $this->stripe->findOrCreateCustomer()->id; - $data['country'] = $this->stripe->client->country->iso_3166_2; - - $intent = \Stripe\PaymentIntent::create([ - 'amount' => $data['stripe_amount'], - 'currency' => 'eur', - 'payment_method_types' => ['ideal'], - 'customer' => $this->stripe->findOrCreateCustomer(), - 'description' => $this->stripe->getDescription(false), - 'metadata' => [ - 'payment_hash' => $this->stripe->payment_hash->hash, - 'gateway_type_id' => GatewayType::IDEAL, - ], - ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); - - $data['pi_client_secret'] = $intent->client_secret; - - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); - $this->stripe->payment_hash->save(); + $data = $this->paymentData($data); return render('gateways.stripe.ideal.pay', $data); } @@ -94,8 +71,8 @@ class iDeal $this->stripe->init(); //catch duplicate submissions. - if (Payment::where('transaction_reference', $payment_intent)->exists()) { - return redirect()->route('client.payments.index'); + if ($pay_exists = Payment::query()->where('transaction_reference', $payment_intent)->first()) { + return redirect()->route('client.payments.show', ['payment' => $pay_exists->hashed_id]); } $data = [ @@ -106,7 +83,7 @@ class iDeal 'gateway_type_id' => GatewayType::IDEAL, ]; - $this->stripe->createPayment($data, Payment::STATUS_COMPLETED); + $payment = $this->stripe->createPayment($data, Payment::STATUS_COMPLETED); SystemLogger::dispatch( ['response' => $this->stripe->payment_hash->data, 'data' => $data], @@ -117,7 +94,7 @@ class iDeal $this->stripe->client->company, ); - return redirect()->route('client.payments.index'); + return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]); } public function processUnsuccessfulPayment() @@ -142,4 +119,40 @@ class iDeal throw new PaymentFailed('Failed to process the payment.', 500); } + + public function paymentData(array $data): array + { + $this->stripe->init(); + + $data['gateway'] = $this->stripe; + $data['return_url'] = $this->buildReturnUrl(); + $data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()); + $data['client'] = $this->stripe->client; + $data['customer'] = $this->stripe->findOrCreateCustomer()->id; + $data['country'] = $this->stripe->client->country->iso_3166_2; + + $intent = \Stripe\PaymentIntent::create([ + 'amount' => $data['stripe_amount'], + 'currency' => 'eur', + 'payment_method_types' => ['ideal'], + 'customer' => $this->stripe->findOrCreateCustomer(), + 'description' => $this->stripe->getDescription(false), + 'metadata' => [ + 'payment_hash' => $this->stripe->payment_hash->hash, + 'gateway_type_id' => GatewayType::IDEAL, + ], + ], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st", true)])); + + $data['pi_client_secret'] = $intent->client_secret; + + $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); + $this->stripe->payment_hash->save(); + + return $data; + } + + public function livewirePaymentView(array $data): string + { + return 'gateways.stripe.ideal.pay_livewire'; + } } diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index d91e39fc3763..220cc1fc504b 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -126,8 +126,8 @@ class StripePaymentDriver extends BaseDriver ); Stripe::setApiKey($this->company_gateway->getConfigField('apiKey')); - Stripe::setApiVersion('2022-11-15'); - // Stripe::setAPiVersion('2023-08-16'); + // Stripe::setApiVersion('2022-11-15'); + Stripe::setAPiVersion('2023-08-16'); } return $this; @@ -419,6 +419,33 @@ class StripePaymentDriver extends BaseDriver return $this->payment_method->paymentView($data); } + public function processPaymentViewData(array $data): array + { + $data = $this->payment_method->paymentData($data); + + $data['stripe_account_id'] = $this->company_gateway->getConfigField('account_id'); + + if (array_key_exists('intent', $data)) { + $data['client_secret'] = $data['intent']->client_secret; + } + + unset($data['intent']); + + $token_billing_string = 'true'; + + if($this->company_gateway->token_billing == 'off' || $this->company_gateway->token_billing == 'optin') { + $token_billing_string = 'false'; + } + + if (isset($data['pre_payment']) && $data['pre_payment'] == '1' && isset($data['is_recurring']) && $data['is_recurring'] == '1') { + $token_billing_string = 'true'; + } + + $data['token_billing_string'] = $token_billing_string; + + return $data; + } + public function processPaymentResponse($request) { return $this->payment_method->paymentResponse($request); @@ -1021,4 +1048,6 @@ class StripePaymentDriver extends BaseDriver return false; } + } + diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 54b43d130ee5..f75f151e5b71 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -326,7 +326,7 @@ class BaseRepository try{ $model->service()->getEInvoice(); } - catch(\Exception $e){ + catch(\Throwable $e){ nlog("EXCEPTION:: BASEREPOSITORY:: Error generating e_invoice for model {$model->id}"); nlog($e->getMessage()); } diff --git a/app/Repositories/ClientRepository.php b/app/Repositories/ClientRepository.php index dc77bb4827e9..b392413c9443 100644 --- a/app/Repositories/ClientRepository.php +++ b/app/Repositories/ClientRepository.php @@ -76,13 +76,6 @@ class ClientRepository extends BaseRepository $client->country_id = $company->settings->country_id; } - if(isset($data['e_invoice']) && is_array($data['e_invoice'])) { - //ensure it is normalized first! - $data['e_invoice'] = $client->arrayFilterRecursive($data['e_invoice']); - - $client->e_invoice = $data['e_invoice']; - } - $client->save(); if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) { diff --git a/app/Repositories/CompanyRepository.php b/app/Repositories/CompanyRepository.php index 44bf6b2cbe9b..dab97852441c 100644 --- a/app/Repositories/CompanyRepository.php +++ b/app/Repositories/CompanyRepository.php @@ -57,11 +57,8 @@ class CompanyRepository extends BaseRepository if(isset($data['smtp_password'])) { $company->smtp_password = $data['smtp_password']; } - - if(isset($data['e_invoice']) && is_array($data['e_invoice'])){ - //ensure it is normalized first! - $data['e_invoice'] = $company->arrayFilterRecursive($data['e_invoice']); - + + if (isset($data['e_invoice'])) { $company->e_invoice = $data['e_invoice']; } diff --git a/app/Repositories/DocumentRepository.php b/app/Repositories/DocumentRepository.php index a59cb2fe991c..e9c104c2a935 100644 --- a/app/Repositories/DocumentRepository.php +++ b/app/Repositories/DocumentRepository.php @@ -22,6 +22,9 @@ class DocumentRepository extends BaseRepository { $document->deleteFile(); $document->forceDelete(); + + if($document->documentable) + $document->documentable->touch(); } public function restore($document) diff --git a/app/Repositories/TaskStatusRepository.php b/app/Repositories/TaskStatusRepository.php index edfbac42c466..0062af553294 100644 --- a/app/Repositories/TaskStatusRepository.php +++ b/app/Repositories/TaskStatusRepository.php @@ -72,6 +72,9 @@ class TaskStatusRepository extends BaseRepository } elseif($ts->status_order >= $task_status->status_order) { $ts->status_order++; $ts->save(); + } else { + $ts->status_order = 99999; + $ts->save(); } }); diff --git a/app/Services/Client/PaymentMethod.php b/app/Services/Client/PaymentMethod.php index 3c19d6e46caf..4aae450790b7 100644 --- a/app/Services/Client/PaymentMethod.php +++ b/app/Services/Client/PaymentMethod.php @@ -70,7 +70,7 @@ class PaymentMethod $transformed_ids = $this->transformKeys(explode(',', $company_gateways)); //gateways disabled - if($company_gateways == '0') { + if ($company_gateways == '0') { $transformed_ids = []; } @@ -105,7 +105,7 @@ class PaymentMethod if ($company_gateways || $company_gateways == '0') { $transformed_ids = $this->transformKeys(explode(',', $company_gateways)); - if($company_gateways == '0') { + if ($company_gateways == '0') { $transformed_ids = []; } @@ -158,7 +158,7 @@ class PaymentMethod $this->payment_methods = $payment_methods_collections->intersectByKeys($payment_methods_collections->flatten(1)->unique()); //@15-06-2024 - foreach($this->payment_methods as $key => $type) { + foreach ($this->payment_methods as $key => $type) { foreach ($type as $gateway_id => $gateway_type_id) { $gate = $this->gateways->where('id', $gateway_id)->first(); $this->buildUrl($gate, $gateway_type_id); diff --git a/app/Services/Client/RFFService.php b/app/Services/Client/RFFService.php index 08b4b3b467a0..f5b5f6ae9f1d 100644 --- a/app/Services/Client/RFFService.php +++ b/app/Services/Client/RFFService.php @@ -108,7 +108,6 @@ class RFFService if ($return_errors) { return $validator->getMessageBag()->getMessages(); } - session()->flash('validation_errors', $validator->getMessageBag()->getMessages()); return false; @@ -129,7 +128,7 @@ class RFFService $contact = []; MultiDB::setDb($this->database); - + foreach ($data as $field => $value) { if (Str::startsWith($field, 'client_')) { $client[$this->mappings[$field]] = $value; diff --git a/app/Services/ClientPortal/LivewireInstantPayment.php b/app/Services/ClientPortal/LivewireInstantPayment.php index e08a61759eb9..25c2726f083c 100644 --- a/app/Services/ClientPortal/LivewireInstantPayment.php +++ b/app/Services/ClientPortal/LivewireInstantPayment.php @@ -56,7 +56,6 @@ class LivewireInstantPayment 'payload' => [], 'component' => '', ]; - /** * is_credit_payment * @@ -64,7 +63,6 @@ class LivewireInstantPayment * @var bool */ private $is_credit_payment = false; - /** * __construct * @@ -89,7 +87,6 @@ class LivewireInstantPayment public function run() { - nlog($this->data); $company_gateway = CompanyGateway::query()->find($this->data['company_gateway_id']); @@ -98,7 +95,6 @@ class LivewireInstantPayment } $payable_invoices = collect($this->data['payable_invoices']); - $tokens = []; $invoices = Invoice::query() @@ -239,7 +235,6 @@ class LivewireInstantPayment $this->mergeResponder(['success' => true, 'component' => 'CreditPaymentComponent', 'payload' => $data]); return $this->getResponder(); - } $this->mergeResponder(['success' => true, 'payload' => $data]); diff --git a/app/Services/EDocument/Gateway/MutatorInterface.php b/app/Services/EDocument/Gateway/MutatorInterface.php new file mode 100644 index 000000000000..65174ce7ff34 --- /dev/null +++ b/app/Services/EDocument/Gateway/MutatorInterface.php @@ -0,0 +1,71 @@ +mutator->getPeppol(); + + if(isset($peppol->PaymentMeans)) { + return $this; + } elseif($paymentMeans = $this->getSetting('Invoice.PaymentMeans')) { + $peppol->PaymentMeans = is_array($paymentMeans) ? $paymentMeans : [$paymentMeans]; + $this->mutator->setPeppol($peppol); + return $this; + } + + return $this->checkRequired($required, "Payment Means"); + + } + + + /** + * getClientSetting + * + * @param string $property_path + * @return mixed + */ + public function getClientSetting(string $property_path): mixed + { + return PropertyResolver::resolve($this->mutator->getClientSettings(), $property_path); + } + + /** + * getCompanySetting + * + * @param string $property_path + * @return mixed + */ + public function getCompanySetting(string $property_path): mixed + { + return PropertyResolver::resolve($this->mutator->getCompanySettings(), $property_path); + } + + /** + * getSetting + * + * Attempts to harvest and return a preconfigured prop from company / client / invoice settings + * + * @param string $property_path + * @return mixed + */ + public function getSetting(string $property_path): mixed + { + + if($prop_value = PropertyResolver::resolve($this->mutator->getPeppol(), $property_path)) { + return $prop_value; + } elseif($prop_value = PropertyResolver::resolve($this->mutator->getClientSettings(), $property_path)) { + return $prop_value; + } elseif($prop_value = PropertyResolver::resolve($this->mutator->getCompanySettings(), $property_path)) { + return $prop_value; + } + return null; + + } + + /** + * Check Required + * + * Throws if a required field is missing. + * + * @param bool $required + * @param string $section + * @return self + */ + public function checkRequired(bool $required, string $section): self + { + return $required ? throw new PeppolValidationException("e-invoice generation halted:: {$section} required", $section, 400) : $this; + } + + /** + * setCustomerAssignedAccountId + * + * Sets the client id_number CAN rely on settings + * + * @param bool $required + * @return self + */ + public function setCustomerAssignedAccountId(bool $required = false): self + { + $peppol = $this->mutator->getPeppol(); + $invoice = $this->mutator->getInvoice(); + + //@phpstan-ignore-next-line + if(isset($peppol->AccountingCustomerParty->CustomerAssignedAccountID)) { + return $this; + } elseif($customer_assigned_account_id = $this->getSetting('Invoice.AccountingCustomerParty.CustomerAssignedAccountID')) { + + $peppol->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; + $this->mutator->setPeppol($peppol); + return $this; + } elseif(strlen($invoice->client->id_number ?? '') > 1) { + + $customer_assigned_account_id = new CustomerAssignedAccountID(); + $customer_assigned_account_id->value = $invoice->client->id_number; + + $peppol->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; + return $this; + } + + //@phpstan-ignore-next-line + return $this->checkRequired($required, 'Client ID Number'); + + } + +} diff --git a/app/Services/EDocument/Gateway/Qvalia/Invoice.php b/app/Services/EDocument/Gateway/Qvalia/Invoice.php new file mode 100644 index 000000000000..66973879e28a --- /dev/null +++ b/app/Services/EDocument/Gateway/Qvalia/Invoice.php @@ -0,0 +1,94 @@ +qvalia->httpClient($uri, (\App\Enum\HttpVerb::GET)->value, []); + + return $r->object(); + } + + /** + * send + * + * @param string $legal_entity_id + * @param string $document + * @return mixed + */ + public function send(string $legal_entity_id, string $document) + { + // Set Headers + // Either "application/json" (default) or "application/xml" + + $headers = [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + // 'Content-Type' => 'application/xml', + ]; + + $data = [ + 'Invoice' => $document + ]; + + $uri = "/transaction/{$legal_entity_id}/invoices/outgoing"; + + $r = $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::POST)->value, $data, $headers); + + return $r->object(); + + } +} diff --git a/app/Services/EDocument/Gateway/Qvalia/Mutator.php b/app/Services/EDocument/Gateway/Qvalia/Mutator.php new file mode 100644 index 000000000000..4af130c55ac4 --- /dev/null +++ b/app/Services/EDocument/Gateway/Qvalia/Mutator.php @@ -0,0 +1,187 @@ +mutator_util = new MutatorUtil($this); + } + + public function setInvoice($invoice): self + { + $this->invoice = $invoice; + return $this; + } + + public function setPeppol($p_invoice): self + { + $this->p_invoice = $p_invoice; + return $this; + } + + public function getPeppol(): mixed + { + return $this->p_invoice; + } + + public function getClientSettings(): mixed + { + return $this->_client_settings; + } + + public function getCompanySettings(): mixed + { + return $this->_company_settings; + } + + public function setClientSettings($client_settings): self + { + $this->_client_settings = $client_settings; + return $this; + } + + public function setCompanySettings($company_settings): self + { + $this->_company_settings = $company_settings; + return $this; + } + + public function getInvoice(): mixed + { + return $this->invoice; + } + + public function getSetting(string $property_path): mixed + { + return $this->mutator_util->getSetting($property_path); + } + + /** + * senderSpecificLevelMutators + * + * Runs sender level specific requirements for the e-invoice, + * + * ie, mutations that are required by the senders country. + * + * @return self + */ + public function senderSpecificLevelMutators(): self + { + + if(method_exists($this, $this->invoice->company->country()->iso_3166_2)) { + $this->{$this->invoice->company->country()->iso_3166_2}(); + } + + return $this; + } + + /** + * receiverSpecificLevelMutators + * + * Runs receiver level specific requirements for the e-invoice + * + * ie mutations that are required by the receiving country + * @return self + */ + public function receiverSpecificLevelMutators(): self + { + + if(method_exists($this, "client_{$this->invoice->company->country()->iso_3166_2}")) { + $this->{"client_{$this->invoice->company->country()->iso_3166_2}"}(); + } + + return $this; + } + + // Country-specific methods + public function DE(): self + { + return $this; + } + public function CH(): self + { + return $this; + } + public function AT(): self + { + return $this; + } + public function AU(): self + { + return $this; + } + public function ES(): self + { + return $this; + } + public function FI(): self + { + return $this; + } + public function FR(): self + { + return $this; + } + public function IT(): self + { + return $this; + } + public function client_IT(): self + { + return $this; + } + public function MY(): self + { + return $this; + } + public function NL(): self + { + return $this; + } + public function NZ(): self + { + return $this; + } + public function PL(): self + { + return $this; + } + public function RO(): self + { + return $this; + } + public function SG(): self + { + return $this; + } + public function SE(): self + { + return $this; + } + +} diff --git a/app/Services/EDocument/Gateway/Qvalia/Partner.php b/app/Services/EDocument/Gateway/Qvalia/Partner.php new file mode 100644 index 000000000000..4cb82f65f52a --- /dev/null +++ b/app/Services/EDocument/Gateway/Qvalia/Partner.php @@ -0,0 +1,144 @@ +partner_number = config('ninja.qvalia_partner_number'); + } + + /** + * getAccount + * + * Get Partner Account Object + * @return mixed + */ + public function getAccount() + { + $uri = "/partner/{$this->partner_number}/account"; + + $r = $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::GET)->value, []); + + return $r->object(); + } + + /** + * getPeppolId + * + * Get information on a peppol ID + * @param string $id + * @return mixed + */ + public function getPeppolId(string $id) + { + $uri = "/partner/{$this->partner_number}/peppol/lookup/{$id}"; + + $uri = "/partner/{$this->partner_number}/account"; + + $r = $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::GET)->value, []); + + return $r->object(); + + } + + /** + * getAccountId + * + * Get information on a Invoice Ninja Peppol Client Account + * @param string $id + * @return mixed + */ + public function getAccountId(string $id) + { + $uri = "/partner/{$this->partner_number}/account/{$id}"; + } + + /** + * createAccount + * + * Create a new account for the partner + * @param array $data + * @return mixed + */ + public function createAccount(array $data) + { + $uri = "/partner/{$this->partner_number}/account"; + + return $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::POST)->value, $data)->object(); + } + + /** + * updateAccount + * + * Update an existing account for the partner + * @param string $accountRegNo + * @param array $data + * @return mixed + */ + public function updateAccount(string $accountRegNo, array $data) + { + $uri = "/partner/{$this->partner_number}/account/{$accountRegNo}"; + + return $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::PUT)->value, $data)->object(); + } + + /** + * deleteAccount + * + * Delete an account for the partner + * @param string $accountRegNo + * @return mixed + */ + public function deleteAccount(string $accountRegNo) + { + $uri = "/partner/{$this->partner_number}/account/{$accountRegNo}"; + + return $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::DELETE)->value)->object(); + } + + /** + * updatePeppolId + * + * Update a Peppol ID for an account + * @param string $accountRegNo + * @param string $peppolId + * @param array $data + * @return mixed + */ + public function updatePeppolId(string $accountRegNo, string $peppolId, array $data) + { + $uri = "/partner/{$this->partner_number}/account/{$accountRegNo}/peppol/{$peppolId}"; + + return $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::PUT)->value, $data)->object(); + } + + /** + * deletePeppolId + * + * Delete a Peppol ID for an account + * @param string $accountRegNo + * @param string $peppolId + * @return mixed + */ + public function deletePeppolId(string $accountRegNo, string $peppolId) + { + $uri = "/partner/{$this->partner_number}/account/{$accountRegNo}/peppol/{$peppolId}"; + + return $this->qvalia->httpClient($uri, (\App\Enum\HttpVerb::DELETE)->value)->object(); + } + +} diff --git a/app/Services/EDocument/Gateway/Qvalia/Qvalia.php b/app/Services/EDocument/Gateway/Qvalia/Qvalia.php new file mode 100644 index 000000000000..5d5c88f86c11 --- /dev/null +++ b/app/Services/EDocument/Gateway/Qvalia/Qvalia.php @@ -0,0 +1,125 @@ + ["invoice"], + "network" => "peppol", + "metaScheme" => "iso6523-actorid-upis", + "scheme" => "de:lwid", + "identifier" => "DE:VAT" + ]; + + /** @var array $dbn_discovery */ + private array $dbn_discovery = [ + "documentTypes" => ["invoice"], + "network" => "dbnalliance", + "metaScheme" => "iso6523-actorid-upis", + "scheme" => "gln", + "identifier" => "1200109963131" + ]; + + private ?int $legal_entity_id; + + public Partner $partner; + + public Invoice $invoice; + + public Mutator $mutator; + //integrationid - returned in headers + + public function __construct() + { + $this->init(); + $this->partner = new Partner($this); + $this->invoice = new Invoice($this); + $this->mutator = new Mutator($this); + } + + private function init(): self + { + + if($this->test_mode) + $this->base_url = $this->sandbox_base_url; + + return $this; + } + + public function sendDocument($legal_entity_id) + { + $uri = "/transaction/{$legal_entity_id}/invoices/outgoing"; + $verb = 'POST'; + } + + + /** + * httpClient + * + * @param string $uri + * @param string $verb + * @param array $data + * @param array $headers + * @return \Illuminate\Http\Client\Response + */ + public function httpClient(string $uri, string $verb, array $data, ?array $headers = []) + { + + try { + $r = Http::withToken(config('ninja.qvalia_api_key')) + ->withHeaders($this->getHeaders($headers)) + ->{$verb}("{$this->base_url}{$uri}", $data)->throw(); + } + catch (ClientException $e) { + // 4xx errors + + nlog("LEI:: {$this->legal_entity_id}"); + nlog("Client error: " . $e->getMessage()); + nlog("Response body: " . $e->getResponse()->getBody()->getContents()); + } catch (ServerException $e) { + // 5xx errors + + nlog("LEI:: {$this->legal_entity_id}"); + nlog("Server error: " . $e->getMessage()); + nlog("Response body: " . $e->getResponse()->getBody()->getContents()); + } catch (\Illuminate\Http\Client\RequestException $e) { + + nlog("LEI:: {$this->legal_entity_id}"); + nlog("Request error: {$e->getCode()}: " . $e->getMessage()); + $responseBody = $e->response->body(); + nlog("Response body: " . $responseBody); + + return $e->response; + + } + + return $r; // @phpstan-ignore-line + } +} diff --git a/app/Services/EDocument/Gateway/Storecove/Mutator.php b/app/Services/EDocument/Gateway/Storecove/Mutator.php new file mode 100644 index 000000000000..fd964fb2a752 --- /dev/null +++ b/app/Services/EDocument/Gateway/Storecove/Mutator.php @@ -0,0 +1,641 @@ +mutator_util = new MutatorUtil($this); + } + + /** + * setInvoice + * + * @param mixed $invoice + * @return self + */ + public function setInvoice($invoice): self + { + $this->invoice = $invoice; + return $this; + } + + /** + * setPeppol + * + * @param mixed $p_invoice + * @return self + */ + public function setPeppol($p_invoice): self + { + $this->p_invoice = $p_invoice; + return $this; + } + + /** + * getPeppol + * + * @return mixed + */ + public function getPeppol(): mixed + { + return $this->p_invoice; + } + + /** + * setClientSettings + * + * @param mixed $client_settings + * @return self + */ + public function setClientSettings($client_settings): self + { + $this->_client_settings = $client_settings; + return $this; + } + + /** + * setCompanySettings + * + * @param mixed $company_settings + * @return self + */ + public function setCompanySettings($company_settings): self + { + $this->_company_settings = $company_settings; + return $this; + } + + public function getClientSettings(): mixed + { + return $this->_client_settings; + } + + public function getCompanySettings(): mixed + { + return $this->_company_settings; + } + + public function getInvoice(): mixed + { + return $this->invoice; + } + + public function getSetting(string $property_path): mixed + { + return $this->mutator_util->getSetting($property_path); + } + /** + * senderSpecificLevelMutators + * + * Runs sender level specific requirements for the e-invoice, + * + * ie, mutations that are required by the senders country. + * + * @return self + */ + public function senderSpecificLevelMutators(): self + { + + if(method_exists($this, $this->invoice->company->country()->iso_3166_2)) { + $this->{$this->invoice->company->country()->iso_3166_2}(); + } + + return $this; + } + + /** + * receiverSpecificLevelMutators + * + * Runs receiver level specific requirements for the e-invoice + * + * ie mutations that are required by the receiving country + * @return self + */ + public function receiverSpecificLevelMutators(): self + { + + if(method_exists($this, "client_{$this->invoice->company->country()->iso_3166_2}")) { + $this->{"client_{$this->invoice->company->country()->iso_3166_2}"}(); + } + + return $this; + } + + /** + * DE + * + * @Completed + * @Tested + * + * @return self + */ + public function DE(): self + { + + $this->mutator_util->setPaymentMeans(true); + + return $this; + } + + /** + * CH + * + * @Completed + * + * Completed - QR-Bill to be implemented at a later date. + * @return self + */ + public function CH(): self + { + return $this; + } + + /** + * AT + * + * @Pending + * + * Need to ensure when sending to government entities that we route appropriately + * Also need to ensure customerAssignedAccountIdValue is set so that the sender can be resolved. + * + * Need a way to define if the client is a government entity. + * + * @return self + */ + public function AT(): self + { + //special fields for sending to AT:GOV + + if($this->invoice->client->classification == 'government') { + //routing "b" for production "test" for test environment + $this->setStorecoveMeta($this->buildRouting(["scheme" => 'AT:GOV', "id" => 'b'])); + + //for government clients this must be set. + $this->mutator_util->setCustomerAssignedAccountId(true); + } + + return $this; + } + + public function AU(): self + { + + //if payment means are included, they must be the same `type` + return $this; + } + + /** + * ES + * + * @Pending + * B2G configuration + * B2G Testing + * + * testing. // routing identifier - 293098 + * + * @return self + */ + public function ES(): self + { + + if(!isset($this->invoice->due_date)) { + $this->p_invoice->DueDate = new \DateTime($this->invoice->date); + } + + if($this->invoice->client->classification == 'business' && $this->invoice->company->getSetting('classification') == 'business') { + //must have a paymentmeans as credit_transfer + $this->mutator_util->setPaymentMeans(true); + } + + // For B2G, provide three ES:FACE identifiers in the routing object, + // as well as the ES:VAT tax identifier in the accountingCustomerParty.publicIdentifiers. + // The invoice will then be routed through the FACe network. The three required ES:FACE identifiers are as follows: + // "routing": { + // "eIdentifiers":[ + // { + // "scheme": "ES:FACE", + // "id": "L01234567", + // "role": "ES-01-FISCAL" + // }, + // { + // "scheme": "ES:FACE", + // "id": "L01234567", + // "role": "ES-02-RECEPTOR" + // }, + // { + // "scheme": "ES:FACE", + // "id": "L01234567", + // "role": "ES-03-PAGADOR" + // } + // ] + // } + + return $this; + } + + /** + * FI + * + * @return self + */ + public function FI(): self + { + + // For Finvoice, provide an FI:OPID routing identifier and an FI:OVT legal identifier. + // An FI:VAT is recommended. In many cases (depending on the sender/receiver country and the type of service/goods) + // an FI:VAT is required. So we recommend always including this. + + return $this; + } + + /** + * FR + * @Pending - clarification on codes needed + * + * @return self + */ + public function FR(): self + { + + // When sending invoices to the French government (Chorus Pro): + // All invoices have to be routed to SIRET 0009:11000201100044. There is no test environment for sending to public entities. + // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. + + if($this->invoice->client->classification == 'government') { + //route to SIRET 0009:11000201100044 + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'FR:SIRET', "id" => '11000201100044'] + + // ["scheme" => 'FR:SIRET', "id" => '0009:11000201100044'] + ])); + + // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. + $this->mutator_util->setCustomerAssignedAccountId(true); + + } + + if(strlen($this->invoice->client->id_number ?? '') == 9) { + //SIREN + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] + + // ["scheme" => 'FR:SIRET', "id" => "0002:{$this->invoice->client->id_number}"] + ])); + } else { + //SIRET + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] + + // ["scheme" => 'FR:SIRET', "id" => "0009:{$this->invoice->client->id_number}"] + ])); + } + + return $this; + } + + /** + * IT + * + * @return self + */ + public function IT(): self + { + + // IT Sender, IT Receiver, B2B/B2G + // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) + if(in_array($this->invoice->client->classification, ['business','government']) && $this->invoice->company->country()->iso_3166_2 == 'IT') { + + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'IT:IVA', "id" => $this->invoice->client->vat_number], + ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] + ])); + + return $this; + } + + // IT Sender, IT Receiver, B2C + // Provide the receiver IT:CF and the receiver IT:CUUO (codice destinatario) + if($this->invoice->client->classification == 'individual' && $this->invoice->company->country()->iso_3166_2 == 'IT') { + + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'IT:CF', "id" => $this->invoice->client->vat_number], + // ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] + ])); + + $this->setEmailRouting($this->invoice->client->present()->email()); + + return $this; + } + + // IT Sender, non-IT Receiver + // Provide the receiver tax identifier and any routing identifier applicable to the receiving country (see Receiver Identifiers). + if($this->invoice->client->country->iso_3166_2 != 'IT' && $this->invoice->company->country()->iso_3166_2 == 'IT') { + + $code = $this->getClientRoutingCode(); + + nlog("foreign receiver"); + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => $code, "id" => $this->invoice->client->vat_number] + ])); + + return $this; + } + + return $this; + } + + /** + * client_IT + * + * @return self + */ + public function client_IT(): self + { + + // non-IT Sender, IT Receiver, B2C + // Provide the receiver IT:CF and an optional email. The invoice will be eReported and sent via email. Note that this cannot be a PEC email address. + if(in_array($this->invoice->client->classification, ['individual']) && $this->invoice->company->country()->iso_3166_2 != 'IT') { + + return $this; + } + + // non-IT Sender, IT Receiver, B2B/B2G + // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) + + return $this; + + } + + /** + * MY + * + * @return self + */ + public function MY(): self + { + //way too much to digest here, delayed. + return $this; + } + + /** + * NL + * + * @return self + */ + public function NL(): self + { + + // When sending to public entities, the invoice.accountingSupplierParty.party.contact.email is mandatory. + + // Dutch senders and receivers require a legal identifier. For companies, this is NL:KVK, for public entities this is NL:OINO. + + return $this; + } + + /** + * NZ + * + * @return self + */ + public function NZ(): self + { + // New Zealand uses a GLN to identify businesses. In addition, when sending invoices to a New Zealand customer, make sure you include the pseudo identifier NZ:GST as their tax identifier. + return $this; + } + + /** + * PL + * + * @return self + */ + public function PL(): self + { + + // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: + + // "routing": { + // "eIdentifiers": [ + // { + // "scheme": "PL:VAT", + // "id": "PL0101010101" + // } + // ], + // "networks": [ + // { + // "application": "pl-ksef", + // "settings": { + // "enabled": true + // } + // } + // ] + // } + // Note this will only work if your LegalEntity has been setup for this network. + + return $this; + } + + /** + * RO + * + * @return self + */ + public function RO(): self + { + // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: + $meta = ["networks" => [ + [ + "application" => "ro-anaf", + "settings" => [ + "enabled" => true + ], + ], + ]]; + + $this->setStorecoveMeta($meta); + + $this->setStorecoveMeta($this->buildRouting([ + ["scheme" => 'RO:VAT', "id" => $this->invoice->client->vat_number], + ])); + + $ro = new RO($this->invoice); + + $client_state = $this->mutator_util->getClientSetting('Invoice.AccountingSupplierParty.Party.PostalAddress.Address.CountrySubentity'); + $client_city = $this->mutator_util->getClientSetting('Invoice.AccountingCustomerParty.Party.PostalAddress.Address.CityName'); + + $resolved_state = $ro->getStateCode($client_state); + $resolved_city = $ro->getSectorCode($client_city); + + $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CountrySubentity = $resolved_state; + $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CityName = $resolved_city; + $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CountrySubentity = $resolved_state; + $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CityName = $resolved_city; + + return $this; + } + + /** + * SG + * + * @return self + */ + public function SG(): self + { + //delayed - stage 2 + return $this; + } + + //Sweden + public function SE(): self + { + // Deliver invoices to the "Svefaktura" co-operation of local Swedish service providers. + // Routing is through the SE:ORGNR together with a network specification: + + // "routing": { + // "eIdentifiers": [ + // { + // "scheme": "SE:ORGNR", + // "id": "0012345678" + // } + // ], + // "networks": [ + // { + // "application": "svefaktura", + // "settings": { + // "enabled": true + // } + // } + // ] + // } + // Use of the "Svefaktura" co-operation can also be induced by specifying an operator id, as follows: + + // "routing": { + // "eIdentifiers": [ + // { + // "scheme": "SE:ORGNR", + // "id": "0012345678" + // }, + // { + // "scheme": "SE:OPID", + // "id": "1234567890" + // } + // ] + // } + return $this; + } + + + /////////////// Storecove Helpers /////////////// + + /** + * getClientRoutingCode + * + * @return string + */ + private function getClientRoutingCode(): string + { + return (new StorecoveRouter())->resolveRouting($this->invoice->client->country->iso_3166_2, $this->invoice->client->classification); + } + + + /** + * Builds the Routing object for StoreCove + * + * @param array $identifiers + * @return array + */ + private function buildRouting(array $identifiers): array + { + return + [ + "routing" => [ + "eIdentifiers" => + $identifiers, + + ] + ]; + } + + + /** + * setEmailRouting + * + * @param string $email + * @return self + */ + private function setEmailRouting(string $email): self + { + + $meta = $this->getStorecoveMeta(); + + if(isset($meta['routing']['emails'])) { + $emails = $meta['routing']['emails']; + array_push($emails, $email); + $meta['routing']['emails'] = $emails; + } else { + $meta['routing']['emails'] = [$email]; + } + + $this->setStorecoveMeta($meta); + + return $this; + } + + + + /** + * setStorecoveMeta + * + * updates the storecove payload for sending documents + * + * @param array $meta + * @return self + */ + private function setStorecoveMeta(array $meta): self + { + + $this->storecove_meta = array_merge($this->storecove_meta, $meta); + + return $this; + } + + /** + * getStorecoveMeta + * + * @return array + */ + public function getStorecoveMeta(): array + { + return $this->storecove_meta; + } + + +} \ No newline at end of file diff --git a/app/Services/EDocument/Gateway/Storecove/Storecove.php b/app/Services/EDocument/Gateway/Storecove/Storecove.php index d362eea22f26..b9d4744055be 100644 --- a/app/Services/EDocument/Gateway/Storecove/Storecove.php +++ b/app/Services/EDocument/Gateway/Storecove/Storecove.php @@ -51,11 +51,16 @@ class Storecove "identifier" => "1200109963131" ]; + private ?int $legal_entity_id; + public StorecoveRouter $router; + public Mutator $mutator; + public function __construct() { $this->router = new StorecoveRouter(); + $this->mutator = new Mutator($this); } /** @@ -129,6 +134,7 @@ class Storecove */ public function sendDocument(string $document, int $routing_id, array $override_payload = []) { + $this->legal_entity_id = $routing_id; $payload = [ "legalEntityId" => $routing_id, @@ -348,21 +354,32 @@ class Storecove private function httpClient(string $uri, string $verb, array $data, ?array $headers = []) { - try { + try { $r = Http::withToken(config('ninja.storecove_api_key')) - ->withHeaders($this->getHeaders($headers)) - ->{$verb}("{$this->base_url}{$uri}", $data)->throw(); + ->withHeaders($this->getHeaders($headers)) + ->{$verb}("{$this->base_url}{$uri}", $data)->throw(); } catch (ClientException $e) { // 4xx errors + + nlog("LEI:: {$this->legal_entity_id}"); nlog("Client error: " . $e->getMessage()); nlog("Response body: " . $e->getResponse()->getBody()->getContents()); } catch (ServerException $e) { // 5xx errors + + nlog("LEI:: {$this->legal_entity_id}"); nlog("Server error: " . $e->getMessage()); nlog("Response body: " . $e->getResponse()->getBody()->getContents()); - } catch (RequestException $e) { + } catch (\Illuminate\Http\Client\RequestException $e) { + + nlog("LEI:: {$this->legal_entity_id}"); nlog("Request error: {$e->getCode()}: " . $e->getMessage()); + $responseBody = $e->response->body(); + nlog("Response body: " . $responseBody); + + return $e->response; + } return $r; // @phpstan-ignore-line diff --git a/app/Services/EDocument/Jobs/SendEDocument.php b/app/Services/EDocument/Jobs/SendEDocument.php index 7528fc227a42..faf4f774c80b 100644 --- a/app/Services/EDocument/Jobs/SendEDocument.php +++ b/app/Services/EDocument/Jobs/SendEDocument.php @@ -51,11 +51,6 @@ class SendEDocument implements ShouldQueue $model = $this->entity::find($this->id); - // $e_invoice_standard = $model->client ? $model->client->getSetting('e_invoice_type') : $model->company->getSetting('e_invoice_type'); - - // if($e_invoice_standard != 'PEPPOL') - // return; - if(Ninja::isSelfHost() && ($model instanceof Invoice) && $model->company->legal_entity_id) { diff --git a/app/Services/EDocument/Standards/Peppol.php b/app/Services/EDocument/Standards/Peppol.php index bce608b94c48..cbddc55bd129 100644 --- a/app/Services/EDocument/Standards/Peppol.php +++ b/app/Services/EDocument/Standards/Peppol.php @@ -13,15 +13,20 @@ namespace App\Services\EDocument\Standards; use App\Models\Company; use App\Models\Invoice; +use App\Models\Product; use App\Helpers\Invoice\Taxer; use App\Services\AbstractService; use App\Helpers\Invoice\InvoiceSum; use InvoiceNinja\EInvoice\EInvoice; use App\Utils\Traits\NumberFormatter; use App\Helpers\Invoice\InvoiceSumInclusive; +use App\Exceptions\PeppolValidationException; use App\Services\EDocument\Standards\Peppol\RO; +use App\Http\Requests\Client\StoreClientRequest; +use App\Services\EDocument\Gateway\Qvalia\Qvalia; use InvoiceNinja\EInvoice\Models\Peppol\PaymentMeans; use InvoiceNinja\EInvoice\Models\Peppol\ItemType\Item; +use App\Services\EDocument\Gateway\Storecove\Storecove; use InvoiceNinja\EInvoice\Models\Peppol\PartyType\Party; use InvoiceNinja\EInvoice\Models\Peppol\PriceType\Price; use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\ID; @@ -39,7 +44,9 @@ use InvoiceNinja\EInvoice\Models\Peppol\PartyNameType\PartyName; use InvoiceNinja\EInvoice\Models\Peppol\TaxSchemeType\TaxScheme; use InvoiceNinja\EInvoice\Models\Peppol\AmountType\PayableAmount; use InvoiceNinja\EInvoice\Models\Peppol\AmountType\TaxableAmount; +use InvoiceNinja\EInvoice\Models\Peppol\PeriodType\InvoicePeriod; use InvoiceNinja\EInvoice\Models\Peppol\TaxTotal as PeppolTaxTotal; +use InvoiceNinja\EInvoice\Models\Peppol\CodeType\IdentificationCode; use InvoiceNinja\EInvoice\Models\Peppol\InvoiceLineType\InvoiceLine; use InvoiceNinja\EInvoice\Models\Peppol\TaxCategoryType\TaxCategory; use InvoiceNinja\EInvoice\Models\Peppol\TaxSubtotalType\TaxSubtotal; @@ -60,6 +67,9 @@ class Peppol extends AbstractService use Taxer; use NumberFormatter; + + //@todo - refactor and move storecove specific logic to the Storecove class + /** * Assumptions: * @@ -237,6 +247,49 @@ class Peppol extends AbstractService "Other" => ["B","DUNS, GLN, LEI",false,"DUNS, GLN, LEI"], ]; + private array $tax_codes = [ + 'AE' => [ + 'name' => 'Vat Reverse Charge', + 'description' => 'Code specifying that the standard VAT rate is levied from the invoicee.' + ], + 'E' => [ + 'name' => 'Exempt from Tax', + 'description' => 'Code specifying that taxes are not applicable.' + ], + 'S' => [ + 'name' => 'Standard rate', + 'description' => 'Code specifying the standard rate.' + ], + 'Z' => [ + 'name' => 'Zero rated goods', + 'description' => 'Code specifying that the goods are at a zero rate.' + ], + 'G' => [ + 'name' => 'Free export item, VAT not charged', + 'description' => 'Code specifying that the item is free export and taxes are not charged.' + ], + 'O' => [ + 'name' => 'Services outside scope of tax', + 'description' => 'Code specifying that taxes are not applicable to the services.' + ], + 'K' => [ + 'name' => 'VAT exempt for EEA intra-community supply of goods and services', + 'description' => 'A tax category code indicating the item is VAT exempt due to an intra-community supply in the European Economic Area.' + ], + 'L' => [ + 'name' => 'Canary Islands general indirect tax', + 'description' => 'Impuesto General Indirecto Canario (IGIC) is an indirect tax levied on goods and services supplied in the Canary Islands (Spain) by traders and professionals, as well as on import of goods.' + ], + 'M' => [ + 'name' => 'Tax for production, services and importation in Ceuta and Melilla', + 'description' => 'Impuesto sobre la Producción, los Servicios y la Importación (IPSI) is an indirect municipal tax, levied on the production, processing and import of all kinds of movable tangible property, the supply of services and the transfer of immovable property located in the cities of Ceuta and Melilla.' + ], + 'B' => [ + 'name' => 'Transferred (VAT), In Italy', + 'description' => 'VAT not to be paid to the issuer of the invoice but directly to relevant tax authority. This code is allowed in the EN 16931 for Italy only based on the Italian A-deviation.' + ] + ]; + private Company $company; private InvoiceSum | InvoiceSumInclusive $calc; @@ -249,7 +302,9 @@ class Peppol extends AbstractService private EInvoice $e; - private array $storecove_meta = []; + private string $api_network = Qvalia::class; // Storecove::class; // Qvalia::class; + + public Qvalia | Storecove $gateway; /** * @param Invoice $invoice @@ -259,6 +314,7 @@ class Peppol extends AbstractService $this->company = $invoice->company; $this->calc = $this->invoice->calc(); $this->e = new EInvoice(); + $this->gateway = new $this->api_network; $this->setSettings()->setInvoice(); } @@ -272,20 +328,45 @@ class Peppol extends AbstractService $this->p_invoice->ID = $this->invoice->number; $this->p_invoice->IssueDate = new \DateTime($this->invoice->date); - if($this->invoice->due_date) { + if($this->invoice->due_date) $this->p_invoice->DueDate = new \DateTime($this->invoice->due_date); + + if(strlen($this->invoice->public_notes ?? '') > 0) + $this->p_invoice->Note = $this->invoice->public_notes; + + $this->p_invoice->DocumentCurrencyCode = $this->invoice->client->currency()->code; + + + if ($this->invoice->date && $this->invoice->due_date) { + $ip = new InvoicePeriod(); + $ip->StartDate = new \DateTime($this->invoice->date); + $ip->EndDate = new \DateTime($this->invoice->due_date); + $this->p_invoice->InvoicePeriod[] = $ip; } + + if ($this->invoice->project_id) { + $pr = new \InvoiceNinja\EInvoice\Models\Peppol\ProjectReferenceType\ProjectReference(); + $id = new \InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\ID(); + $id->value = $this->invoice->project->number; + $pr->ID = $id; + $this->p_invoice->ProjectReference[] = $pr; + } + $this->p_invoice->InvoiceTypeCode = ($this->invoice->amount >= 0) ? 380 : 381; // $this->p_invoice->AccountingSupplierParty = $this->getAccountingSupplierParty(); $this->p_invoice->AccountingCustomerParty = $this->getAccountingCustomerParty(); $this->p_invoice->InvoiceLine = $this->getInvoiceLines(); - // $this->p_invoice->TaxTotal = $this->getTotalTaxes(); it only wants the aggregate here!! $this->p_invoice->LegalMonetaryTotal = $this->getLegalMonetaryTotal(); - $this->senderSpecificLevelMutators() - ->receiverSpecificLevelMutators(); + $this->setOrderReference(); + + $this->p_invoice = $this->gateway + ->mutator + ->senderSpecificLevelMutators() + ->receiverSpecificLevelMutators() + ->getPeppol(); if(strlen($this->invoice->backup ?? '') == 0) { @@ -305,9 +386,9 @@ class Peppol extends AbstractService private function setInvoice(): self { - if($this->invoice->e_invoice) { + if($this->invoice->e_invoice && isset($this->invoice->e_invoice->Invoice)) { - $this->p_invoice = $this->e->decode('Peppol', json_encode($this->invoice->e_invoice), 'json'); + $this->p_invoice = $this->e->decode('Peppol', json_encode($this->invoice->e_invoice->Invoice), 'json'); return $this; @@ -315,6 +396,13 @@ class Peppol extends AbstractService $this->p_invoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice(); + $this->gateway + ->mutator + ->setInvoice($this->invoice) + ->setPeppol($this->p_invoice) + ->setClientSettings($this->_client_settings) + ->setCompanySettings($this->_company_settings); + $this->setInvoiceDefaults(); return $this; @@ -363,6 +451,8 @@ class Peppol extends AbstractService xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">'; + nlog($xml); + return str_ireplace(['\n',''], ['', $prefix], $xml); } @@ -388,7 +478,11 @@ class Peppol extends AbstractService */ public function toObject(): mixed { - return json_decode($this->toJson()); + + $invoice = new \stdClass; + $invoice->Invoice = json_decode($this->toJson()); + return $invoice; + } /** @@ -398,9 +492,31 @@ class Peppol extends AbstractService */ public function toArray(): array { - return json_decode($this->toJson(), true); + return ['Invoice' => json_decode($this->toJson(), true)]; } + + private function setOrderReference(): self + { + + $this->p_invoice->BuyerReference = $this->invoice->po_number ?? ''; + + if (strlen($this->invoice->po_number ?? '') > 1) { + $order_reference = new OrderReference(); + $id = new ID(); + $id->value = $this->invoice->po_number; + + $order_reference->ID = $id; + + $this->p_invoice->OrderReference = $order_reference; + + + } + + return $this; + + } + /** * getLegalMonetaryTotal * @@ -436,143 +552,59 @@ class Peppol extends AbstractService } /** - * getTotalTaxAmount + * getTaxType * - * @return float + * @param string $tax_id + * @return string */ - private function getTotalTaxAmount(): float + private function getTaxType(string $tax_id = ''): string { - if(!$this->invoice->total_taxes) { - return 0; - } elseif($this->invoice->uses_inclusive_taxes) { - return $this->invoice->total_taxes; + $tax_type = null; + + switch ($tax_id) { + case Product::PRODUCT_TYPE_SERVICE: + case Product::PRODUCT_TYPE_DIGITAL: + case Product::PRODUCT_TYPE_PHYSICAL: + case Product::PRODUCT_TYPE_SHIPPING: + case Product::PRODUCT_TYPE_REDUCED_TAX: + $tax_type = 'S'; + break; + case Product::PRODUCT_TYPE_EXEMPT: + $tax_type = 'E'; + break; + case Product::PRODUCT_TYPE_ZERO_RATED: + $tax_type = 'Z'; + break; + case Product::PRODUCT_TYPE_REVERSE_TAX: + $tax_type = 'AE'; + break; } - return $this->calcAmountLineTax($this->invoice->tax_rate1, $this->invoice->amount) ?? 0; - } - - /** - * getTotalTaxes - * - * @return array - */ - private function getTotalTaxes(): array - { - $taxes = []; - - $type_id = $this->invoice->line_items[0]->type_id; - - // if(strlen($this->invoice->tax_name1 ?? '') > 1) { - - $tax_amount = new TaxAmount(); - $tax_amount->currencyID = $this->invoice->client->currency()->code; - $tax_amount->amount = $this->getTotalTaxAmount(); - - $tax_subtotal = new TaxSubtotal(); - $tax_subtotal->TaxAmount = $tax_amount; - - $taxable_amount = new TaxableAmount(); - $taxable_amount->currencyID = $this->invoice->client->currency()->code; - $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->invoice->amount - $this->invoice->total_taxes : $this->invoice->amount; - $tax_subtotal->TaxableAmount = $taxable_amount; - - $tc = new TaxCategory(); - $id = new ID(); - $id->value = $type_id == '2' ? 'HUR' : 'C62'; - $tc->ID = $id; - $tc->Percent = $this->invoice->tax_rate1; - $ts = new TaxScheme(); - $id = new ID(); - $id->value = strlen($this->invoice->tax_name1 ?? '') > 1 ? $this->invoice->tax_name1 : '0'; - $ts->ID = $id; - $tc->TaxScheme = $ts; - $tax_subtotal->TaxCategory = $tc; - - $tax_total = new TaxTotal(); - $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal[] = $tax_subtotal; - - $taxes[] = $tax_total; - // } - - - if(strlen($this->invoice->tax_name2 ?? '') > 1) { - - $tax_amount = new TaxAmount(); - $tax_amount->currencyID = $this->invoice->client->currency()->code; - - $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate2, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate2, $this->invoice->amount); - - $tax_subtotal = new TaxSubtotal(); - $tax_subtotal->TaxAmount = $tax_amount; - - $taxable_amount = new TaxableAmount(); - $taxable_amount->currencyID = $this->invoice->client->currency()->code; - $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->invoice->amount - $this->invoice->total_taxes : $this->invoice->amount; - $tax_subtotal->TaxableAmount = $taxable_amount; - - - $tc = new TaxCategory(); - $id = new ID(); - $id->value = $type_id == '2' ? 'HUR' : 'C62'; - $tc->ID = $id; - $tc->Percent = $this->invoice->tax_rate2; - $ts = new TaxScheme(); - $id = new ID(); - $id->value = $this->invoice->tax_name2; - $ts->ID = $id; - $tc->TaxScheme = $ts; - $tax_subtotal->TaxCategory = $tc; - - - $tax_total = new TaxTotal(); - $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal[] = $tax_subtotal; - - $taxes[] = $tax_total; - + $eu_states = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "EL", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "ES-CE", "ES-ML", "ES-CN", "SE", "IS", "LI", "NO", "CH"]; + + if (empty($tax_type)) { + if ((in_array($this->company->country()->iso_3166_2, $eu_states) && in_array($this->invoice->client->country->iso_3166_2, $eu_states)) && $this->invoice->company->country()->iso_3166_2 != $this->invoice->client->country->iso_3166_2) { + $tax_type = 'K'; //EEA Exempt + } elseif (!in_array($this->invoice->client->country->iso_3166_2, $eu_states)) { + $tax_type = 'G'; //Free export item, VAT not charged + } else { + $tax_type = 'S'; //Standard rate + } } - if(strlen($this->invoice->tax_name3 ?? '') > 1) { - - $tax_amount = new TaxAmount(); - $tax_amount->currencyID = $this->invoice->client->currency()->code; - $tax_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->calcInclusiveLineTax($this->invoice->tax_rate3, $this->invoice->amount) : $this->calcAmountLineTax($this->invoice->tax_rate3, $this->invoice->amount); - - $tax_subtotal = new TaxSubtotal(); - $tax_subtotal->TaxAmount = $tax_amount; - - $taxable_amount = new TaxableAmount(); - $taxable_amount->currencyID = $this->invoice->client->currency()->code; - $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $this->invoice->amount - $this->invoice->total_taxes : $this->invoice->amount; - $tax_subtotal->TaxableAmount = $taxable_amount; - - $tc = new TaxCategory(); + if(in_array($this->invoice->client->country->iso_3166_2, ["ES-CE", "ES-ML", "ES-CN"]) && $tax_type == 'S') { - $id = new ID(); - $id->value = $type_id == '2' ? 'HUR' : 'C62'; - $tc->ID = $id; - $tc->Percent = $this->invoice->tax_rate3; - $ts = new TaxScheme(); - - $id = new ID(); - $id->value = $this->invoice->tax_name3; - - $ts->ID = $id; - $tc->TaxScheme = $ts; - $tax_subtotal->TaxCategory = $tc; - - $tax_total = new TaxTotal(); - $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal[] = $tax_subtotal; - - $taxes[] = $tax_total; + if ($this->invoice->client->country->iso_3166_2 == "ES-CN") { + $tax_type = 'L'; //Canary Islands general indirect tax + } elseif (in_array($this->invoice->client->country->iso_3166_2, ["ES-CE", "ES-ML"])) { + $tax_type = 'M'; //Tax for production, services and importation in Ceuta and Melilla + } } - return $taxes; + return $tax_type; } - + private function getInvoiceLines(): array { $lines = []; @@ -583,6 +615,35 @@ class Peppol extends AbstractService $_item->Name = $item->product_key; $_item->Description = $item->notes; + + if($item->tax_rate1 > 0) + { + $ctc = new ClassifiedTaxCategory(); + $ctc->ID = new ID(); + $ctc->ID->value = $this->getTaxType($item->tax_id); + $ctc->Percent = $item->tax_rate1; + + $_item->ClassifiedTaxCategory[] = $ctc; + } + + if ($item->tax_rate2 > 0) { + $ctc = new ClassifiedTaxCategory(); + $ctc->ID = new ID(); + $ctc->ID->value = $this->getTaxType($item->tax_id); + $ctc->Percent = $item->tax_rate2; + + $_item->ClassifiedTaxCategory[] = $ctc; + } + + if ($item->tax_rate3 > 0) { + $ctc = new ClassifiedTaxCategory(); + $ctc->ID = new ID(); + $ctc->ID->value = $this->getTaxType($item->tax_id); + $ctc->Percent = $item->tax_rate3; + + $_item->ClassifiedTaxCategory[] = $ctc; + } + $line = new InvoiceLine(); $id = new ID(); @@ -592,7 +653,6 @@ class Peppol extends AbstractService $lea = new LineExtensionAmount(); $lea->currencyID = $this->invoice->client->currency()->code; - // $lea->amount = $item->line_total; $lea->amount = $this->invoice->uses_inclusive_taxes ? $item->line_total - $this->calcInclusiveLineTax($item->tax_rate1, $item->line_total) : $item->line_total; $line->LineExtensionAmount = $lea; $line->Item = $_item; @@ -602,9 +662,6 @@ class Peppol extends AbstractService if(count($item_taxes) > 0) { $line->TaxTotal = $item_taxes; } - // else { - // $line->TaxTotal = $this->zeroTaxAmount(); - // } $price = new Price(); $pa = new PriceAmount(); @@ -620,6 +677,7 @@ class Peppol extends AbstractService return $lines; } + /** * costWithDiscount * @@ -646,41 +704,42 @@ class Peppol extends AbstractService * * @return array */ - private function zeroTaxAmount(): array - { - $blank_tax = []; + // private function zeroTaxAmount(): array + // { + // $blank_tax = []; - $tax_amount = new TaxAmount(); - $tax_amount->currencyID = $this->invoice->client->currency()->code; - $tax_amount->amount = '0'; - $tax_subtotal = new TaxSubtotal(); - $tax_subtotal->TaxAmount = $tax_amount; + // $tax_amount = new TaxAmount(); + // $tax_amount->currencyID = $this->invoice->client->currency()->code; + // $tax_amount->amount = '0'; + // $tax_subtotal = new TaxSubtotal(); + // $tax_subtotal->TaxAmount = $tax_amount; - $taxable_amount = new TaxableAmount(); - $taxable_amount->currencyID = $this->invoice->client->currency()->code; - $taxable_amount->amount = '0'; - $tax_subtotal->TaxableAmount = $taxable_amount; - $tc = new TaxCategory(); - $id = new ID(); - $id->value = 'Z'; - $tc->ID = $id; - $tc->Percent = '0'; - $ts = new TaxScheme(); + // $taxable_amount = new TaxableAmount(); + // $taxable_amount->currencyID = $this->invoice->client->currency()->code; + // $taxable_amount->amount = '0'; + // $tax_subtotal->TaxableAmount = $taxable_amount; + + // $tc = new TaxCategory(); + // $id = new ID(); + // $id->value = 'Z'; + // $tc->ID = $id; + // $tc->Percent = '0'; + // $ts = new TaxScheme(); - $id = new ID(); - $id->value = '0'; - $ts->ID = $id; - $tc->TaxScheme = $ts; - $tax_subtotal->TaxCategory = $tc; + // $id = new ID(); + // $id->value = '0'; + // $ts->ID = $id; + // $tc->TaxScheme = $ts; + // $tax_subtotal->TaxCategory = $tc; - $tax_total = new TaxTotal(); - $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal[] = $tax_subtotal; - $blank_tax[] = $tax_total; + // $tax_total = new TaxTotal(); + // $tax_total->TaxAmount = $tax_amount; + // $tax_total->TaxSubtotal[] = $tax_subtotal; + // $blank_tax[] = $tax_total; - return $blank_tax; - } + // return $blank_tax; + // } /** * getItemTaxes @@ -704,11 +763,13 @@ class Peppol extends AbstractService $taxable_amount->currencyID = $this->invoice->client->currency()->code; $taxable_amount->amount = $this->invoice->uses_inclusive_taxes ? $item->line_total - $tax_amount->amount : $item->line_total; $tax_subtotal->TaxableAmount = $taxable_amount; + + $tc = new TaxCategory(); $id = new ID(); - $id->value = $item->type_id == '2' ? 'HUR' : 'C62'; - + $id->value = $this->getTaxType($item->tax_id); + $tc->ID = $id; $tc->Percent = $item->tax_rate1; $ts = new TaxScheme(); @@ -720,7 +781,6 @@ class Peppol extends AbstractService $tc->TaxScheme = $ts; $tax_subtotal->TaxCategory = $tc; - $tax_total = new TaxTotal(); $tax_total->TaxAmount = $tax_amount; $tax_total->TaxSubtotal[] = $tax_subtotal; @@ -748,7 +808,7 @@ class Peppol extends AbstractService $tc = new TaxCategory(); $id = new ID(); - $id->value = $item->type_id == '2' ? 'HUR' : 'C62'; + $id->value = $this->getTaxType($item->tax_id); $tc->ID = $id; $tc->Percent = $item->tax_rate2; @@ -789,7 +849,7 @@ class Peppol extends AbstractService $tc = new TaxCategory(); $id = new ID(); - $id->value = $item->type_id == '2' ? 'HUR' : 'C62'; + $id->value = $this->getTaxType($item->tax_id); $tc->ID = $id; $tc->Percent = $item->tax_rate3; @@ -837,16 +897,20 @@ class Peppol extends AbstractService // $address->CountrySubentityCode = $this->invoice->company->settings->state; $country = new Country(); - $country->IdentificationCode = $this->invoice->company->country()->iso_3166_2; + + $ic = new IdentificationCode(); + $ic->value = substr($this->invoice->company->country()->iso_3166_2, 0, 2); + $country->IdentificationCode = $ic; + $address->Country = $country; $party->PostalAddress = $address; $party->PhysicalLocation = $address; $contact = new Contact(); - $contact->ElectronicMail = $this->getSetting('Invoice.AccountingSupplierParty.Party.Contact') ?? $this->invoice->company->owner()->present()->email(); - $contact->Telephone = $this->getSetting('Invoice.AccountingSupplierParty.Party.Telephone') ?? $this->invoice->company->getSetting('phone'); - $contact->Name = $this->getSetting('Invoice.AccountingSupplierParty.Party.Name') ?? $this->invoice->company->owner()->present()->name(); + $contact->ElectronicMail = $this->gateway->mutator->getSetting('Invoice.AccountingSupplierParty.Party.Contact') ?? $this->invoice->company->owner()->present()->email(); + $contact->Telephone = $this->gateway->mutator->getSetting('Invoice.AccountingSupplierParty.Party.Telephone') ?? $this->invoice->company->getSetting('phone'); + $contact->Name = $this->gateway->mutator->getSetting('Invoice.AccountingSupplierParty.Party.Name') ?? $this->invoice->company->owner()->present()->name(); $party->Contact = $contact; @@ -863,30 +927,6 @@ class Peppol extends AbstractService private function resolveTaxScheme(): string { return (new StorecoveRouter())->resolveTaxScheme($this->invoice->client->country->iso_3166_2, $this->invoice->client->classification); - - // $rules = isset($this->routing_rules[$this->invoice->client->country->iso_3166_2]) ? $this->routing_rules[$this->invoice->client->country->iso_3166_2] : [false, false, false, false,]; - - // $code = false; - - // match($this->invoice->client->classification) { - // "business" => $code = "B", - // "government" => $code = "G", - // "individual" => $code = "C", - // default => $code = false, - // }; - - // //single array - // if(is_array($rules) && !is_array($rules[0])) { - // return $rules[2]; - // } - - // foreach($rules as $rule) { - // if(stripos($rule[0], $code) !== false) { - // return $rule[2]; - // } - // } - - // return false; } /** @@ -930,9 +970,12 @@ class Peppol extends AbstractService $address->CountrySubentity = $this->invoice->client->state; // $address->CountrySubentityCode = $this->invoice->client->state; - $country = new Country(); - $country->IdentificationCode = $this->invoice->client->country->iso_3166_2; + + $ic = new IdentificationCode(); + $ic->value = substr($this->invoice->client->country->iso_3166_2, 0, 2); + + $country->IdentificationCode = $ic; $address->Country = $country; $party->PostalAddress = $address; @@ -1006,33 +1049,6 @@ class Peppol extends AbstractService ///////////////// Helper Methods ///////////////////////// - /** - * getClientRoutingCode - * - * @return string - */ - private function getClientRoutingCode(): string - { - // $receiver_identifiers = $this->routing_rules[$this->invoice->client->country->iso_3166_2]; - // $client_classification = $this->invoice->client->classification == 'government' ? 'G' : 'B'; - - // if(count($receiver_identifiers) > 1) { - - // foreach($receiver_identifiers as $ident) { - // if(str_contains($ident[0], $client_classification)) { - // return $ident[3]; - // } - // } - - // } elseif(count($receiver_identifiers) == 1) { - // return $receiver_identifiers[3]; - // } - - return (new StorecoveRouter())->resolveRouting($this->invoice->client->country->iso_3166_2, $this->invoice->client->classification); - - // throw new \Exception("e-invoice generation halted:: Could not resolve the Tax Code for this client? {$this->invoice->client->hashed_id}"); - - } /** * setInvoiceDefaults @@ -1042,706 +1058,50 @@ class Peppol extends AbstractService */ public function setInvoiceDefaults(): self { - $settings = [ - 'AccountingCostCode' => 7, - 'AccountingCost' => 7, - 'BuyerReference' => 6, - 'AccountingSupplierParty' => 1, - 'AccountingCustomerParty' => 2, - 'PayeeParty' => 1, - 'BuyerCustomerParty' => 2, - 'SellerSupplierParty' => 1, - 'TaxRepresentativeParty' => 1, - 'Delivery' => 1, - 'DeliveryTerms' => 7, - 'PaymentMeans' => 7, - 'PaymentTerms' => 7, - ]; - //only scans for top level props - foreach($settings as $prop => $visibility) { - - if($prop_value = $this->getSetting($prop)) { - $this->p_invoice->{$prop} = $prop_value; + // Stub new invoice with company settings. + if($this->_company_settings) + { + foreach(get_object_vars($this->_company_settings) as $prop => $value){ + $this->p_invoice->{$prop} = $value; + } } - } + // Overwrite with any client level settings + if($this->_client_settings) + { + foreach (get_object_vars($this->_client_settings) as $prop => $value) { + $this->p_invoice->{$prop} = $value; + } + } - return $this; - } + // Plucks special overriding properties scanning the correct settings level + $settings = [ + 'AccountingCostCode' => 7, + 'AccountingCost' => 7, + 'BuyerReference' => 6, + 'AccountingSupplierParty' => 1, + 'AccountingCustomerParty' => 2, + 'PayeeParty' => 1, + 'BuyerCustomerParty' => 2, + 'SellerSupplierParty' => 1, + 'TaxRepresentativeParty' => 1, + 'Delivery' => 1, + 'DeliveryTerms' => 7, + 'PaymentMeans' => 7, + 'PaymentTerms' => 7, + ]; - /** - * getSetting - * - * Attempts to harvest and return a preconfigured prop from company / client / invoice settings - * - * @param string $property_path - * @return mixed - */ - public function getSetting(string $property_path): mixed - { + //only scans for top level props + foreach($settings as $prop => $visibility) { - if($prop_value = PropertyResolver::resolve($this->p_invoice, $property_path)) { - return $prop_value; - } elseif($prop_value = PropertyResolver::resolve($this->_client_settings, $property_path)) { - return $prop_value; - } elseif($prop_value = PropertyResolver::resolve($this->_company_settings, $property_path)) { - return $prop_value; - } - return null; + if($prop_value = $this->gateway->mutator->getSetting($prop)) { + $this->p_invoice->{$prop} = $prop_value; + } - } - - /** - * getClientSetting - * - * @param string $property_path - * @return mixed - */ - private function getClientSetting(string $property_path): mixed - { - return PropertyResolver::resolve($this->_client_settings, $property_path); - } - - /** - * getCompanySetting - * - * @param string $property_path - * @return mixed - */ - private function getCompanySetting(string $property_path): mixed - { - return PropertyResolver::resolve($this->_company_settings, $property_path); - } - - /** - * senderSpecificLevelMutators - * - * Runs sender level specific requirements for the e-invoice, - * - * ie, mutations that are required by the senders country. - * - * @return self - */ - private function senderSpecificLevelMutators(): self - { - - if(method_exists($this, $this->invoice->company->country()->iso_3166_2)) { - $this->{$this->invoice->company->country()->iso_3166_2}(); - } - - return $this; - } - - /** - * receiverSpecificLevelMutators - * - * Runs receiver level specific requirements for the e-invoice - * - * ie mutations that are required by the receiving country - * @return self - */ - private function receiverSpecificLevelMutators(): self - { - - if(method_exists($this, "client_{$this->invoice->company->country()->iso_3166_2}")) { - $this->{"client_{$this->invoice->company->country()->iso_3166_2}"}(); - } - - return $this; - } - - - /** - * setPaymentMeans - * - * Sets the payment means - if it exists - * @param bool $required - * @return self - */ - private function setPaymentMeans(bool $required = false): self - { - - if(isset($this->p_invoice->PaymentMeans)) { - return $this; - } elseif($paymentMeans = $this->getSetting('Invoice.PaymentMeans')) { - $this->p_invoice->PaymentMeans = is_array($paymentMeans) ? $paymentMeans : [$paymentMeans]; - return $this; - } - - return $this->checkRequired($required, "Payment Means"); - - } - - /** - * setOrderReference - * - * sets the order reference - if it exists (Never rely on settings for this) - * - * @param bool $required - * @return self - */ - private function setOrderReference(bool $required = false): self - { - $this->p_invoice->BuyerReference = $this->invoice->po_number ?? ''; - - if(strlen($this->invoice->po_number ?? '') > 1) { - $order_reference = new OrderReference(); - $id = new ID(); - $id->value = $this->invoice->po_number; - - $order_reference->ID = $id; - - $this->p_invoice->OrderReference = $order_reference; - - // $this->setStorecoveMeta(["document" => [ - // "invoice" => [ - // [ - // "references" => [ - // "documentType" => "purchase_order", - // "documentId" => $this->invoice->po_number, - // ], - // ], - // ], - // ] - // ]); + } return $this; - } - - return $this->checkRequired($required, 'Order Reference'); - } - /** - * setCustomerAssignedAccountId - * - * Sets the client id_number CAN rely on settings - * - * @param bool $required - * @return self - */ - private function setCustomerAssignedAccountId(bool $required = false): self - { - //@phpstan-ignore-next-line - if(isset($this->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID)) { - return $this; - } elseif($customer_assigned_account_id = $this->getSetting('Invoice.AccountingCustomerParty.CustomerAssignedAccountID')) { - - $this->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; - return $this; - } elseif(strlen($this->invoice->client->id_number ?? '') > 1) { - - $customer_assigned_account_id = new CustomerAssignedAccountID(); - $customer_assigned_account_id->value = $this->invoice->client->id_number; - - $this->p_invoice->AccountingCustomerParty->CustomerAssignedAccountID = $customer_assigned_account_id; - return $this; - } - - //@phpstan-ignore-next-line - return $this->checkRequired($required, 'Client ID Number'); - - } - - /** - * Check Required - * - * Throws if a required field is missing. - * - * @param bool $required - * @param string $section - * @return self - */ - private function checkRequired(bool $required, string $section): self - { - - return $required ? throw new \Exception("e-invoice generation halted:: {$section} required") : $this; - - } - - - /** - * Builds the Routing object for StoreCove - * - * @param array $identifiers - * @return array - */ - private function buildRouting(array $identifiers): array - { - return - [ - "routing" => [ - "eIdentifiers" => - $identifiers, - - ] - ]; - } - - /** - * setEmailRouting - * - * @param string $email - * @return self - */ - private function setEmailRouting(string $email): self - { - nlog($email); - - $meta = $this->getStorecoveMeta(); - - if(isset($meta['routing']['emails'])) { - $emails = $meta['routing']['emails']; - array_push($emails, $email); - $meta['routing']['emails'] = $emails; - } else { - $meta['routing']['emails'] = [$email]; - } - - $this->setStorecoveMeta($meta); - - return $this; - } - - /** - * setStorecoveMeta - * - * updates the storecove payload for sending documents - * - * @param array $meta - * @return self - */ - private function setStorecoveMeta(array $meta): self - { - - $this->storecove_meta = array_merge($this->storecove_meta, $meta); - - return $this; - } - - /** - * getStorecoveMeta - * - * @return array - */ - public function getStorecoveMeta(): array - { - return $this->storecove_meta; - } - - - - - ////////////////////////// Country level mutators ///////////////////////////////////// - - /** - * DE - * - * @Completed - * @Tested - * - * @return self - */ - private function DE(): self - { - - $this->setPaymentMeans(true); - - return $this; - } - - /** - * CH - * - * @Completed - * - * Completed - QR-Bill to be implemented at a later date. - * @return self - */ - private function CH(): self - { - return $this; - } - - /** - * AT - * - * @Pending - * - * Need to ensure when sending to government entities that we route appropriately - * Also need to ensure customerAssignedAccountIdValue is set so that the sender can be resolved. - * - * Need a way to define if the client is a government entity. - * - * @return self - */ - private function AT(): self - { - //special fields for sending to AT:GOV - - if($this->invoice->client->classification == 'government') { - //routing "b" for production "test" for test environment - $this->setStorecoveMeta($this->buildRouting(["scheme" => 'AT:GOV', "id" => 'b'])); - - //for government clients this must be set. - $this->setCustomerAssignedAccountId(true); - } - - return $this; - } - - private function AU(): self - { - - //if payment means are included, they must be the same `type` - return $this; - } - - /** - * ES - * - * @Pending - * B2G configuration - * B2G Testing - * - * testing. // routing identifier - 293098 - * - * @return self - */ - private function ES(): self - { - - if(!isset($this->invoice->due_date)) { - $this->p_invoice->DueDate = new \DateTime($this->invoice->date); - } - - if($this->invoice->client->classification == 'business' && $this->invoice->company->getSetting('classification') == 'business') { - //must have a paymentmeans as credit_transfer - $this->setPaymentMeans(true); - } - - // For B2G, provide three ES:FACE identifiers in the routing object, - // as well as the ES:VAT tax identifier in the accountingCustomerParty.publicIdentifiers. - // The invoice will then be routed through the FACe network. The three required ES:FACE identifiers are as follows: - // "routing": { - // "eIdentifiers":[ - // { - // "scheme": "ES:FACE", - // "id": "L01234567", - // "role": "ES-01-FISCAL" - // }, - // { - // "scheme": "ES:FACE", - // "id": "L01234567", - // "role": "ES-02-RECEPTOR" - // }, - // { - // "scheme": "ES:FACE", - // "id": "L01234567", - // "role": "ES-03-PAGADOR" - // } - // ] - // } - - return $this; - } - - /** - * FI - * - * @return self - */ - private function FI(): self - { - - // For Finvoice, provide an FI:OPID routing identifier and an FI:OVT legal identifier. - // An FI:VAT is recommended. In many cases (depending on the sender/receiver country and the type of service/goods) - // an FI:VAT is required. So we recommend always including this. - - return $this; - } - - /** - * FR - * @Pending - clarification on codes needed - * - * @return self - */ - private function FR(): self - { - - // When sending invoices to the French government (Chorus Pro): - // All invoices have to be routed to SIRET 0009:11000201100044. There is no test environment for sending to public entities. - // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. - - if($this->invoice->client->classification == 'government') { - //route to SIRET 0009:11000201100044 - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'FR:SIRET', "id" => '11000201100044'] - - // ["scheme" => 'FR:SIRET', "id" => '0009:11000201100044'] - ])); - - // The SIRET / 0009 identifier of the final recipient is to be included in the invoice.accountingCustomerParty.publicIdentifiers array. - $this->setCustomerAssignedAccountId(true); - - } - - if(strlen($this->invoice->client->id_number ?? '') == 9) { - //SIREN - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] - - // ["scheme" => 'FR:SIRET', "id" => "0002:{$this->invoice->client->id_number}"] - ])); - } else { - //SIRET - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'FR:SIRET', "id" => "{$this->invoice->client->id_number}"] - - // ["scheme" => 'FR:SIRET', "id" => "0009:{$this->invoice->client->id_number}"] - ])); - } - - // Apparently this is not a special field according to support - // sounds like it is optional - // The service code must be sent in invoice.buyerReference (deprecated) or the invoice.references array (documentType buyer_reference) - - if(strlen($this->invoice->po_number ?? '') > 1) { - $this->setOrderReference(false); - } - - return $this; - } - - /** - * IT - * - * @return self - */ - private function IT(): self - { - - // IT Sender, IT Receiver, B2B/B2G - // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) - if(in_array($this->invoice->client->classification, ['business','government']) && $this->invoice->company->country()->iso_3166_2 == 'IT') { - - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'IT:IVA', "id" => $this->invoice->client->vat_number], - ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] - ])); - - return $this; - } - - // IT Sender, IT Receiver, B2C - // Provide the receiver IT:CF and the receiver IT:CUUO (codice destinatario) - if($this->invoice->client->classification == 'individual' && $this->invoice->company->country()->iso_3166_2 == 'IT') { - - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'IT:CF', "id" => $this->invoice->client->vat_number], - // ["scheme" => 'IT:CUUO', "id" => $this->invoice->client->routing_id] - ])); - - $this->setEmailRouting($this->invoice->client->present()->email()); - - return $this; - } - - // IT Sender, non-IT Receiver - // Provide the receiver tax identifier and any routing identifier applicable to the receiving country (see Receiver Identifiers). - if($this->invoice->client->country->iso_3166_2 != 'IT' && $this->invoice->company->country()->iso_3166_2 == 'IT') { - - $code = $this->getClientRoutingCode(); - - nlog("foreign receiver"); - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => $code, "id" => $this->invoice->client->vat_number] - ])); - - return $this; - } - - return $this; - } - - /** - * client_IT - * - * @return self - */ - private function client_IT(): self - { - - // non-IT Sender, IT Receiver, B2C - // Provide the receiver IT:CF and an optional email. The invoice will be eReported and sent via email. Note that this cannot be a PEC email address. - if(in_array($this->invoice->client->classification, ['individual']) && $this->invoice->company->country()->iso_3166_2 != 'IT') { - - return $this; - } - - // non-IT Sender, IT Receiver, B2B/B2G - // Provide the receiver IT:VAT and the receiver IT:CUUO (codice destinatario) - - return $this; - - } - - /** - * MY - * - * @return self - */ - private function MY(): self - { - //way too much to digest here, delayed. - return $this; - } - - /** - * NL - * - * @return self - */ - private function NL(): self - { - - // When sending to public entities, the invoice.accountingSupplierParty.party.contact.email is mandatory. - - // Dutch senders and receivers require a legal identifier. For companies, this is NL:KVK, for public entities this is NL:OINO. - - return $this; - } - - /** - * NZ - * - * @return self - */ - private function NZ(): self - { - // New Zealand uses a GLN to identify businesses. In addition, when sending invoices to a New Zealand customer, make sure you include the pseudo identifier NZ:GST as their tax identifier. - return $this; - } - - /** - * PL - * - * @return self - */ - private function PL(): self - { - - // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: - - // "routing": { - // "eIdentifiers": [ - // { - // "scheme": "PL:VAT", - // "id": "PL0101010101" - // } - // ], - // "networks": [ - // { - // "application": "pl-ksef", - // "settings": { - // "enabled": true - // } - // } - // ] - // } - // Note this will only work if your LegalEntity has been setup for this network. - - return $this; - } - - /** - * RO - * - * @return self - */ - private function RO(): self - { - // Because using this network is not yet mandatory, the default workflow is to not use this network. Therefore, you have to force its use, as follows: - $meta = ["networks" => [ - [ - "application" => "ro-anaf", - "settings" => [ - "enabled" => true - ], - ], - ]]; - - $this->setStorecoveMeta($meta); - - $this->setStorecoveMeta($this->buildRouting([ - ["scheme" => 'RO:VAT', "id" => $this->invoice->client->vat_number], - ])); - - $ro = new RO($this->invoice); - - $client_state = $this->getClientSetting('Invoice.AccountingSupplierParty.Party.PostalAddress.Address.CountrySubentity'); - $client_city = $this->getClientSetting('Invoice.AccountingCustomerParty.Party.PostalAddress.Address.CityName'); - - $resolved_state = $ro->getStateCode($client_state); - $resolved_city = $ro->getSectorCode($client_city); - - $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CountrySubentity = $resolved_state; - $this->p_invoice->AccountingCustomerParty->Party->PostalAddress->CityName = $resolved_city; - $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CountrySubentity = $resolved_state; - $this->p_invoice->AccountingCustomerParty->Party->PhysicalLocation->Address->CityName = $resolved_city; - - return $this; - } - - /** - * SG - * - * @return self - */ - private function SG(): self - { - //delayed - stage 2 - return $this; - } - - //Sweden - private function SE(): self - { - // Deliver invoices to the "Svefaktura" co-operation of local Swedish service providers. - // Routing is through the SE:ORGNR together with a network specification: - - // "routing": { - // "eIdentifiers": [ - // { - // "scheme": "SE:ORGNR", - // "id": "0012345678" - // } - // ], - // "networks": [ - // { - // "application": "svefaktura", - // "settings": { - // "enabled": true - // } - // } - // ] - // } - // Use of the "Svefaktura" co-operation can also be induced by specifying an operator id, as follows: - - // "routing": { - // "eIdentifiers": [ - // { - // "scheme": "SE:ORGNR", - // "id": "0012345678" - // }, - // { - // "scheme": "SE:OPID", - // "id": "1234567890" - // } - // ] - // } - return $this; - } } diff --git a/app/Services/EDocument/Standards/Peppol/BaseCountry.php b/app/Services/EDocument/Standards/Peppol/BaseCountry.php new file mode 100644 index 000000000000..e53db73956fc --- /dev/null +++ b/app/Services/EDocument/Standards/Peppol/BaseCountry.php @@ -0,0 +1,23 @@ + 'Alba', diff --git a/app/Services/EDocument/Standards/Validation/Peppol/EntityLevel.php b/app/Services/EDocument/Standards/Validation/Peppol/EntityLevel.php new file mode 100644 index 000000000000..48a92af35077 --- /dev/null +++ b/app/Services/EDocument/Standards/Validation/Peppol/EntityLevel.php @@ -0,0 +1,230 @@ +init($client->locale()); + $this->errors['client'] = $this->testClientState($client); + $this->errors['passes'] = count($this->errors['client']) == 0; + + return $this->errors; + + } + + public function checkCompany(Company $company): array + { + + $this->init($company->locale()); + $this->errors['company'] = $this->testCompanyState($company); + $this->errors['passes'] = count($this->errors['company']) == 0; + + return $this->errors; + + } + + public function checkInvoice(Invoice $invoice): array + { + $this->init($invoice->client->locale()); + + $this->errors['invoice'] = []; + $this->errors['client'] = $this->testClientState($invoice->client); + $this->errors['company'] = $this->testCompanyState($invoice->client); // uses client level settings which is what we want + + $p = new Peppol($invoice); + + try{ + $p->run()->toXml(); + } + catch(PeppolValidationException $e) { + + $this->errors['invoice'] = ['field' => $e->getInvalidField()]; + + }; + + $this->errors['passes'] = count($this->errors['invoice']) == 0 && count($this->errors['client']) == 0 && count($this->errors['company']) == 0; + + return $this->errors; + + } + + private function testClientState(Client $client): array + { + + $errors = []; + + foreach($this->client_fields as $field) + { + + if($this->validString($client->{$field})) + continue; + + if($field == 'country_id' && $client->country_id >=1) + continue; + + $errors[] = ['field' => ctrans("texts.{$field}")]; + + } + + //If not an individual, you MUST have a VAT number + if ($client->classification != 'individual' && !$this->validString($client->vat_number)) { + $errors[] = ['field' => ctrans("texts.vat_number")]; + } + + return $errors; + + } + + private function testCompanyState(mixed $entity): array + { + + $client = false; + $vendor = false; + $settings_object = false; + $company =false; + + if($entity instanceof Client){ + $client = $entity; + $company = $entity->company; + $settings_object = $client; + } + elseif($entity instanceof Company){ + $company = $entity; + $settings_object = $company; + } + elseif($entity instanceof Vendor){ + $vendor = $entity; + $company = $entity->company; + $settings_object = $company; + } + elseif($entity instanceof Invoice || $entity instanceof Credit || $entity instanceof Quote){ + $client = $entity->client; + $company = $entity->company; + $settings_object = $entity->client; + } + elseif($entity instanceof PurchaseOrder){ + $vendor = $entity->vendor; + $company = $entity->company; + $settings_object = $company; + } + + $errors = []; + + foreach($this->company_settings_fields as $field) + { + + if($this->validString($settings_object->getSetting($field))) + continue; + + $errors[] = ['field' => ctrans("texts.{$field}")]; + + } + + //test legal entity id present + if(!is_int($company->legal_entity_id)) + $errors[] = ['field' => "You have not registered a legal entity id as yet."]; + + //If not an individual, you MUST have a VAT number + if($company->getSetting('classification') != 'individual' && !$this->validString($company->getSetting('vat_number'))) + { + $errors[] = ['field' => ctrans("texts.vat_number")]; + } + + // foreach($this->company_fields as $field) + // { + + // } + + return $errors; + + } + + // private function testInvoiceState($entity): array + // { + // $errors = []; + + // foreach($this->invoice_fields as $field) + // { + + // } + + // return $errors; + // } + + // private function testVendorState(): array + // { + + // } + + + /************************************ helpers ************************************/ + private function validString(?string $string): bool + { + return iconv_strlen($string) >= 1; + } + +} \ No newline at end of file diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 3c7fc0bb9c75..178e37c42899 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -98,7 +98,9 @@ class AutoBillInvoice extends AbstractService return $this->invoice; } - info("Auto Bill - balance remains to be paid!! - {$amount}"); + nlog("Auto Bill - balance remains to be paid!! - {$amount}"); + nlog($this->invoice->amount); + nlog($this->invoice->balance); /* Retrieve the Client Gateway Token */ /** @var \App\Models\ClientGatewayToken $gateway_token */ @@ -112,11 +114,15 @@ class AutoBillInvoice extends AbstractService // return $this->invoice; } - nlog('Gateway present - adding gateway fee'); + nlog("Gateway present - adding gateway fee on {$amount}"); /* $gateway fee */ $this->invoice = $this->invoice->service()->addGatewayFee($gateway_token->gateway, $gateway_token->gateway_type_id, $amount)->save(); + + nlog($this->invoice->amount); + nlog($this->invoice->balance); + //change from $this->invoice->amount to $this->invoice->balance if ($is_partial) { $fee = $this->invoice->balance - $invoice_total; @@ -124,6 +130,8 @@ class AutoBillInvoice extends AbstractService $fee = $this->invoice->balance - $amount; } + nlog("fee is {$fee}"); + if ($fee > $amount) { $fee = 0; } @@ -148,8 +156,7 @@ class AutoBillInvoice extends AbstractService ]); nlog("Payment hash created => {$payment_hash->id}"); - $this->invoice->saveQuietly(); - + $payment = false; try { $payment = $gateway_token->gateway @@ -171,8 +178,6 @@ class AutoBillInvoice extends AbstractService $this->invoice->auto_bill_tries = 0; //reset the counter here in case auto billing is turned on again in the future. } - $this->invoice->save(); - if ($payment) { info('Auto Bill payment captured for '.$this->invoice->number); event(new InvoiceAutoBillSuccess($this->invoice, $this->invoice->company, Ninja::eventVars())); diff --git a/app/Services/Payment/RefundPayment.php b/app/Services/Payment/RefundPayment.php index a353c50791f2..4d406754aa73 100644 --- a/app/Services/Payment/RefundPayment.php +++ b/app/Services/Payment/RefundPayment.php @@ -103,6 +103,7 @@ class RefundPayment //block prevents the edge case where a partial refund was attempted. $this->refund_data['invoices'] = $this->payment->invoices->map(function ($invoice) { return [ + 'date' => now()->addSeconds($invoice->client->timezone_offset())->format('Y-m-d'), 'invoice_id' => $invoice->id, 'amount' => $invoice->pivot->amount, ]; diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php index 093b8b15774d..d1edef413238 100644 --- a/app/Services/Pdf/PdfBuilder.php +++ b/app/Services/Pdf/PdfBuilder.php @@ -356,6 +356,37 @@ class PdfBuilder $tbody[] = $element; $this->payment_amount_total += $payment->pivot->amount; + + if($payment->pivot->refunded > 0){ + + $refund_date = $payment->date; + + if($payment->refund_meta && is_array($payment->refund_meta)){ + + $refund_array = collect($payment->refund_meta)->first(function ($meta) use($invoice){ + foreach($meta['invoices'] as $refunded_invoice){ + + if ($refunded_invoice['invoice_id'] == $invoice->id) { + return true; + } + + } + }); + + $refund_date = $refund_array['date']; + } + + $element = ['element' => 'tr', 'elements' => []]; + $element['elements'][] = ['element' => 'td', 'content' => $invoice->number]; + $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($refund_date, $this->service->config->date_format, $this->service->config->locale) ?: ' ']; + $element['elements'][] = ['element' => 'td', 'content' => ctrans('texts.refund')]; + $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($payment->pivot->refunded) ?: ' ']; + + $tbody[] = $element; + + $this->payment_amount_total -= $payment->pivot->refunded; + + } } } diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php index c9b5bfa94ca5..54d11c0a9ac9 100644 --- a/app/Services/PdfMaker/Design.php +++ b/app/Services/PdfMaker/Design.php @@ -561,7 +561,41 @@ class Design extends BaseDesign $tbody[] = $element; $this->payment_amount_total += $payment->pivot->amount; - } + + if ($payment->pivot->refunded > 0) { + + $refund_date = $payment->date; + + if($payment->refund_meta && is_array($payment->refund_meta)){ + + $refund_array = collect($payment->refund_meta)->first(function ($meta) use($invoice){ + foreach($meta['invoices'] as $refunded_invoice){ + + if ($refunded_invoice['invoice_id'] == $invoice->id) { + return true; + } + + } + }); + + $refund_date = $refund_array['date']; + } + + $element = ['element' => 'tr', 'elements' => []]; + $element['elements'][] = ['element' => 'td', 'content' => $invoice->number]; + $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($refund_date, $this->client->date_format(), $this->client->locale()) ?: ' ']; + $element['elements'][] = ['element' => 'td', 'content' => ctrans('texts.refund')]; + $element['elements'][] = ['element' => 'td', 'content' => Number::formatMoney($payment->pivot->refunded, $this->client) ?: ' ']; + + $tbody[] = $element; + + $this->payment_amount_total -= $payment->pivot->refunded; + + } + + } + + } return [ diff --git a/app/Utils/Traits/CleanLineItems.php b/app/Utils/Traits/CleanLineItems.php index e82623bd3f0a..04c084e46ed3 100644 --- a/app/Utils/Traits/CleanLineItems.php +++ b/app/Utils/Traits/CleanLineItems.php @@ -34,6 +34,20 @@ trait CleanLineItems return $cleaned_items; } + public function cleanFeeItems($items): array + { + + //ensure we never allow gateway fees to be cloned across to new entities + foreach ($items as $key => $value) { + if (in_array($value['type_id'], ['3','4'])) { + unset($items[$key]); + } + } + + return $items; + + } + /** * Sets default values for the line_items. * @param $item diff --git a/app/Utils/Traits/SavesDocuments.php b/app/Utils/Traits/SavesDocuments.php index cfca896cb81e..c35aaa6bb833 100644 --- a/app/Utils/Traits/SavesDocuments.php +++ b/app/Utils/Traits/SavesDocuments.php @@ -48,6 +48,8 @@ trait SavesDocuments $is_public ))->handle(); } + + $entity->touch(); } public function saveDocument($document, $entity, $is_public = true) @@ -75,5 +77,8 @@ trait SavesDocuments null, $is_public ))->handle(); + + $entity->touch(); + } } diff --git a/composer.json b/composer.json index 7bd7d745dde4..87b33403ad4e 100644 --- a/composer.json +++ b/composer.json @@ -104,7 +104,7 @@ "symfony/postmark-mailer": "^6.1", "turbo124/beacon": "^2", "twig/intl-extra": "^3.7", - "twig/twig": "^3", + "twig/twig": "^3.14", "twilio/sdk": "^6.40", "wildbit/postmark-php": "^4.0" }, diff --git a/composer.lock b/composer.lock index ddf6dde5869a..09da42d6cc1e 100644 --- a/composer.lock +++ b/composer.lock @@ -535,16 +535,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.321.11", + "version": "3.321.7", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "bbd357d246350ffcd0dd8df30951d2d46c5ddadb" + "reference": "c64ee32d80ec2ab5d8d6a0b77297c2d69602ef3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/bbd357d246350ffcd0dd8df30951d2d46c5ddadb", - "reference": "bbd357d246350ffcd0dd8df30951d2d46c5ddadb", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c64ee32d80ec2ab5d8d6a0b77297c2d69602ef3b", + "reference": "c64ee32d80ec2ab5d8d6a0b77297c2d69602ef3b", "shasum": "" }, "require": { @@ -627,9 +627,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.321.11" + "source": "https://github.com/aws/aws-sdk-php/tree/3.321.7" }, - "time": "2024-09-13T18:05:10+00:00" + "time": "2024-09-09T18:09:23+00:00" }, { "name": "bacon/bacon-qr-code", @@ -4760,7 +4760,73 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-09-13T13:36:30+00:00" + "time": "2024-09-03T15:27:15+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.17.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "9d77be916e145864f10788bb94531d03e1f7b482" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482", + "reference": "9d77be916e145864f10788bb94531d03e1f7b482", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.64.0", + "illuminate/view": "^10.48.20", + "larastan/larastan": "^2.9.8", + "laravel-zero/framework": "^10.4.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^1.15.1", + "pestphp/pest": "^2.35.1" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2024-09-03T15:00:28+00:00" }, { "name": "laravel/prompts", @@ -17228,16 +17294,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.34", + "version": "10.5.33", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3c69d315bdf79080c8e115b69d1961c6905b0e18" + "reference": "4def7a9cda75af9c2bc179ed53a8e41313e7f7cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3c69d315bdf79080c8e115b69d1961c6905b0e18", - "reference": "3c69d315bdf79080c8e115b69d1961c6905b0e18", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4def7a9cda75af9c2bc179ed53a8e41313e7f7cf", + "reference": "4def7a9cda75af9c2bc179ed53a8e41313e7f7cf", "shasum": "" }, "require": { @@ -17309,7 +17375,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.34" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.33" }, "funding": [ { @@ -17325,7 +17391,7 @@ "type": "tidelift" } ], - "time": "2024-09-13T05:19:38+00:00" + "time": "2024-09-09T06:06:56+00:00" }, { "name": "react/cache", diff --git a/config/ninja.php b/config/ninja.php index db302f6dbf0e..5f8576149ef5 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -252,5 +252,7 @@ return [ 'private_key' => env('NINJA_PRIVATE_KEY', false), ], 'upload_extensions' => env('ADDITIONAL_UPLOAD_EXTENSIONS', ''), - 'storecove_api_key' => env('STORECOVE_API_KEY', false), + 'storecove_api_key' => env('STORECOVE_API_KEY', false), + 'qvalia_api_key' => env('QVALIA_API_KEY', false), + 'qvalia_partner_number' => env('QVALIA_PARTNER_NUMBER', false), ]; diff --git a/database/migrations/2024_09_15_022436_add_autonomous_es_regions.php b/database/migrations/2024_09_15_022436_add_autonomous_es_regions.php new file mode 100644 index 000000000000..1b5c4c0a09e7 --- /dev/null +++ b/database/migrations/2024_09_15_022436_add_autonomous_es_regions.php @@ -0,0 +1,118 @@ +string('iso_3166_2',5)->change(); + $table->string('country_code', 4)->change(); + }); + + $regions = [ + [ + 'id' => 1000, // INE code for Canary Islands + 'capital' => 'Las Palmas de Gran Canaria / Santa Cruz de Tenerife', + 'citizenship' => 'Spanish', + 'country_code' => '1000', + 'currency' => 'euro', + 'currency_code' => 'EUR', + 'currency_sub_unit' => 'cent', + 'full_name' => 'Canary Islands', + 'iso_3166_2' => 'ES-CN', + 'iso_3166_3' => 'ESP', // Spain's ISO 3166-3 code + 'name' => 'Canary Islands', + 'region_code' => '142', + 'sub_region_code' => '024', + 'eea' => true, + 'swap_postal_code' => false, + 'swap_currency_symbol' => false, + 'thousand_separator' => '', + 'decimal_separator' => '', + ], + [ + 'id' => 1001, // INE code for Ceuta + 'capital' => 'Ceuta', + 'citizenship' => 'Spanish', + 'country_code' => '1001', + 'currency' => 'euro', + 'currency_code' => 'EUR', + 'currency_sub_unit' => 'cent', + 'full_name' => 'Ceuta', + 'iso_3166_2' => 'ES-CE', + 'iso_3166_3' => 'ESP', // Spain's ISO 3166-3 code + 'name' => 'Ceuta', + 'region_code' => '142', + 'sub_region_code' => '020', + 'eea' => true, + 'swap_postal_code' => false, + 'swap_currency_symbol' => false, + 'thousand_separator' => '', + 'decimal_separator' => '', + ], + [ + 'id' => 1002, // INE code for Melilla + 'capital' => 'Melilla', + 'citizenship' => 'Spanish', + 'country_code' => '1002', + 'currency' => 'euro', + 'currency_code' => 'EUR', + 'currency_sub_unit' => 'cent', + 'full_name' => 'Melilla', + 'iso_3166_2' => 'ES-ML', + 'iso_3166_3' => 'ESP', // Spain's ISO 3166-3 code + 'name' => 'Melilla', + 'region_code' => '142', + 'sub_region_code' => '021', + 'eea' => true, + 'swap_postal_code' => false, + 'swap_currency_symbol' => false, + 'thousand_separator' => '', + 'decimal_separator' => '', + ], + ]; + + Model::unguard(); + + foreach ($regions as $region) { + Country::create($region); + } + + Model::reguard(); + + + Company::query()->cursor()->each(function ($company) { + $company->tax_data = new \App\DataMapper\Tax\TaxModel($company->tax_data); + $company->save(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/database/seeders/CountriesSeeder.php b/database/seeders/CountriesSeeder.php index c23fef933291..3a01922f382f 100644 --- a/database/seeders/CountriesSeeder.php +++ b/database/seeders/CountriesSeeder.php @@ -29,7 +29,7 @@ class CountriesSeeder extends Seeder { Model::unguard(); - $countries = json_decode($this->countries, 1); + $countries = json_decode($this->countries, true); foreach($countries as $country) { diff --git a/lang/en/texts.php b/lang/en/texts.php index 4e55c151a7e7..61fc8268af59 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5330,6 +5330,9 @@ $lang = array( 'payment_failed' => 'Payment Failed', 'ssl_host_override' => 'SSL Host Override', 'upload_logo_short' => 'Upload Logo', + 'country_Melilla' => 'Melilla', + 'country_Ceuta' => 'Ceuta', + 'country_Canary Islands' => 'Canary Islands', ); return $lang; diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index 684db93062f5..ccc86165eb07 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -5312,6 +5312,20 @@ Développe automatiquement la section des notes dans le tableau de produits pour 'comments_only' => 'Commentaires seulement', 'payment_balance_on_file' => 'Payer le solde inscrit au dossier', 'ubl_email_attachment_help' => 'Plus de paramètres pour E-facture, cliquez :here', + 'stop_task_to_add_task_entry' => 'Vous dez arrêter la tâche avant d\'ajouter un nouvel article.', + 'xml_file' => 'Fichier XML', + 'one_page_checkout' => 'Paiement simplifié', + 'one_page_checkout_help' => 'Activer la nouvelle page de paiement simplifiée', + 'applies_to' => 'S\'applique à', + 'accept_purchase_order' => 'Accepter le bon d\'achat', + 'round_to_seconds' => 'Arrondir aux secondes', + 'activity_142' => 'Le rappel 1 de la soumission :number a été envoyé', + 'activity_143' => 'La facturation automatique a réussi pour la facture :invoice', + 'activity_144' => 'La facturation automatique de :invoice_number a échouée. :notes', + 'activity_145' => 'E-Facture :invoice pour :client à été envoyée. :notes', + 'payment_failed' => 'Le paiement a échoué', + 'ssl_host_override' => 'Substitution d\'hôte SSL', + 'upload_logo_short' => 'Téléverser un logo', ); return $lang; diff --git a/lang/ru_RU/texts.php b/lang/ru_RU/texts.php index 880b898e5670..87021a1b8b10 100644 --- a/lang/ru_RU/texts.php +++ b/lang/ru_RU/texts.php @@ -1100,7 +1100,7 @@ $lang = array( 'invoice_embed_documents' => 'Embed Documents', 'invoice_embed_documents_help' => 'Include attached images in the invoice.', 'document_email_attachment' => 'Загрузить Документы', - 'ubl_email_attachment' => 'Attach UBL', + 'ubl_email_attachment' => 'Attach UBL/E-Invoice', 'download_documents' => 'Download Documents (:size)', 'documents_from_expenses' => 'From Expenses:', 'dropzone_default_message' => 'Для загрузки Перетащите файлы или нажмите.', @@ -2365,7 +2365,7 @@ $lang = array( 'currency_gold_troy_ounce' => 'Gold Troy Ounce', 'currency_nicaraguan_córdoba' => 'Nicaraguan Córdoba', 'currency_malagasy_ariary' => 'Malagasy ariary', - "currency_tongan_pa_anga" => "Tongan Pa'anga", + "currency_tongan_paanga" => "Tongan Pa'anga", 'review_app_help' => 'We hope you\'re enjoying using the app.
If you\'d consider :link we\'d greatly appreciate it!', 'writing_a_review' => 'writing a review', @@ -2881,19 +2881,6 @@ $lang = array( 'refunded' => 'Refunded', 'marked_quote_as_sent' => 'Successfully marked quote as sent', 'custom_module_settings' => 'Custom Module Settings', - 'ticket' => 'Тикет', - 'tickets' => 'Тикеты', - 'ticket_number' => 'Ticket #', - 'new_ticket' => 'New Ticket', - 'edit_ticket' => 'Edit Ticket', - 'view_ticket' => 'Посмотреть Тикет', - 'archive_ticket' => 'Архивировать Тикет', - 'restore_ticket' => 'Восстановить Тикет', - 'delete_ticket' => 'Удалить Тикет', - 'archived_ticket' => 'Successfully archived ticket', - 'archived_tickets' => 'Successfully archived tickets', - 'restored_ticket' => 'Successfully restored ticket', - 'deleted_ticket' => 'Successfully deleted ticket', 'open' => 'Открыт', 'new' => 'Новый', 'closed' => 'Закрыт', @@ -2910,14 +2897,6 @@ $lang = array( 'assigned_to' => 'Назначен', 'reply' => 'Ответ', 'awaiting_reply' => 'В ожидании ответа', - 'ticket_close' => 'Закрыть Тикет', - 'ticket_reopen' => 'Открыть Тикет заново', - 'ticket_open' => 'Открыть Тикет', - 'ticket_split' => 'Разделить Тикет', - 'ticket_merge' => 'Объединить Тикет', - 'ticket_update' => 'Обновить Тикет', - 'ticket_settings' => 'Настройки тикета', - 'updated_ticket' => 'Тикет обновлён', 'mark_spam' => 'Пометить как спам', 'local_part' => 'Local Part', 'local_part_unavailable' => 'Name taken', @@ -2935,31 +2914,23 @@ $lang = array( 'mime_types' => 'Mime types', 'mime_types_placeholder' => '.pdf , .docx, .jpg', 'mime_types_help' => 'Comma separated list of allowed mime types, leave blank for all', + 'ticket_number_start_help' => 'Ticket number must be greater than the current ticket number', + 'new_ticket_template_id' => 'New ticket', + 'new_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a new ticket is created', + 'update_ticket_template_id' => 'Updated ticket', + 'update_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a ticket is updated', + 'close_ticket_template_id' => 'Closed ticket', + 'close_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a ticket is closed', 'default_priority' => 'ПРиоритет по-умолчанию', 'alert_new_comment_id' => 'Новый комментарий', - 'alert_comment_ticket_help' => 'Selecting a template will send a notification (to agent) when a comment is made.', - 'alert_comment_ticket_email_help' => 'Comma separated emails to bcc on new comment.', - 'new_ticket_notification_list' => 'Additional new ticket notifications', 'update_ticket_notification_list' => 'Additional new comment notifications', 'comma_separated_values' => 'admin@example.ru, supervisor@example.ru', - 'alert_ticket_assign_agent_id' => 'Ticket assignment', - 'alert_ticket_assign_agent_id_hel' => 'Selecting a template will send a notification (to agent) when a ticket is assigned.', - 'alert_ticket_assign_agent_id_notifications' => 'Additional ticket assigned notifications', - 'alert_ticket_assign_agent_id_help' => 'Comma separated emails to bcc on ticket assignment.', - 'alert_ticket_transfer_email_help' => 'Comma separated emails to bcc on ticket transfer.', - 'alert_ticket_overdue_agent_id' => 'Тикет просрочен', - 'alert_ticket_overdue_email' => 'Additional overdue ticket notifications', - 'alert_ticket_overdue_email_help' => 'Comma separated emails to bcc on ticket overdue.', - 'alert_ticket_overdue_agent_id_help' => 'Selecting a template will send a notification (to agent) when a ticket becomes overdue.', 'default_agent' => 'Default Agent', 'default_agent_help' => 'If selected will automatically be assigned to all inbound tickets', 'show_agent_details' => 'Show agent details on responses', 'avatar' => 'Аватар', 'remove_avatar' => 'Удалить аватар', - 'ticket_not_found' => 'Тикет не найден', 'add_template' => 'Добавить Шаблон', - 'updated_ticket_template' => 'Updated Ticket Template', - 'created_ticket_template' => 'Created Ticket Template', 'archive_ticket_template' => 'Архивировать шаблон', 'restore_ticket_template' => 'Восстановить шаблон', 'archived_ticket_template' => 'Successfully archived template', @@ -3075,7 +3046,7 @@ $lang = array( 'portal_mode' => 'Portal Mode', 'attach_pdf' => 'Attach PDF', 'attach_documents' => 'Attach Documents', - 'attach_ubl' => 'Attach UBL', + 'attach_ubl' => 'Attach UBL/E-Invoice', 'email_style' => 'Email Style', 'processed' => 'Processed', 'fee_amount' => 'Fee Amount', @@ -3816,7 +3787,7 @@ $lang = array( 'entity_number_placeholder' => ':entity # :entity_number', 'email_link_not_working' => 'If the button above isn\'t working for you, please click on the link', 'display_log' => 'Display Log', - 'send_fail_logs_to_our_server' => 'Report errors in realtime', + 'send_fail_logs_to_our_server' => 'Report errors to help improve the app', 'setup' => 'Setup', 'quick_overview_statistics' => 'Quick overview & statistics', 'update_your_personal_info' => 'Update your personal information', @@ -5154,7 +5125,7 @@ $lang = array( 'all_contacts' => 'All Contacts', 'insert_below' => 'Insert Below', 'nordigen_handler_subtitle' => 'Bank account authentication. Selecting your institution to complete the request with your account credentials.', - 'nordigen_handler_error_heading_unknown' => 'An error has occured', + 'nordigen_handler_error_heading_unknown' => 'An error has occurred', 'nordigen_handler_error_contents_unknown' => 'An unknown error has occurred! Reason:', 'nordigen_handler_error_heading_token_invalid' => 'Invalid Token', 'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Contact support for help, if this issue persists.', @@ -5268,7 +5239,7 @@ $lang = array( 'local_domain_help' => 'EHLO domain (optional)', 'port_help' => 'ie. 25,587,465', 'host_help' => 'ie. smtp.gmail.com', - 'always_show_required_fields' => 'Allows show required fields form', + 'always_show_required_fields' => 'Always show required fields form', 'always_show_required_fields_help' => 'Displays the required fields form always at checkout', 'advanced_cards' => 'Advanced Cards', 'activity_140' => 'Statement sent to :client', @@ -5304,6 +5275,60 @@ $lang = array( 'currency_bhutan_ngultrum' => 'Bhutan Ngultrum', 'end_of_month' => 'End Of Month', 'merge_e_invoice_to_pdf' => 'Merge E-Invoice and PDF', + 'task_assigned_subject' => 'New task assignment [Task :task] [ :date ]', + 'task_assigned_body' => 'You have been assigned task :task

Description: :description

Client: :client', + 'activity_141' => 'User :user entered note: :notes', + 'quote_reminder_subject' => 'Reminder: Quote :quote from :company', + 'quote_reminder_message' => 'Reminder for quote :number for :amount', + 'quote_reminder1' => 'First Quote Reminder', + 'before_valid_until_date' => 'Before the valid until date', + 'after_valid_until_date' => 'After the valid until date', + 'after_quote_date' => 'After the quote date', + 'remind_quote' => 'Remind Quote', + 'end_of_month' => 'End Of Month', + 'tax_currency_mismatch' => 'Tax currency is different from invoice currency', + 'edocument_import_already_exists' => 'The invoice has already been imported on :date', + 'before_valid_until' => 'Before the valid until', + 'after_valid_until' => 'After the valid until', + 'task_assigned_notification' => 'Task Assigned Notification', + 'task_assigned_notification_help' => 'Send an email when a task is assigned', + 'invoices_locked_end_of_month' => 'Invoices are locked at the end of the month', + 'referral_url' => 'Referral URL', + 'add_comment' => 'Add Comment', + 'added_comment' => 'Successfully saved comment', + 'tickets' => 'Tickets', + 'assigned_group' => 'Successfully assigned group', + 'merge_to_pdf' => 'Merge to PDF', + 'latest_requires_php_version' => 'Note: the latest version requires PHP :version', + 'auto_expand_product_table_notes' => 'Automatically expand products table notes', + 'auto_expand_product_table_notes_help' => 'Automatically expands the notes section within the products table to display more lines.', + 'institution_number' => 'Institution Number', + 'transit_number' => 'Transit Number', + 'personal' => 'Personal', + 'address_information' => 'Address Information', + 'enter_the_information_for_the_bank_account' => 'Enter the Information for the Bank Account', + 'account_holder_information' => 'Account Holder Information', + 'enter_information_for_the_account_holder' => 'Enter Information for the Account Holder', + 'customer_type' => 'Customer Type', + 'process_date' => 'Process Date', + 'forever_free' => 'Forever Free', + 'comments_only' => 'Comments Only', + 'payment_balance_on_file' => 'Payment Balance On File', + 'ubl_email_attachment_help' => 'For more e-invoice settings please navigate :here', + 'stop_task_to_add_task_entry' => 'You need to stop the task before adding a new item.', + 'xml_file' => 'XML File', + 'one_page_checkout' => 'One-Page Checkout', + 'one_page_checkout_help' => 'Enable the new single page payment flow', + 'applies_to' => 'Applies To', + 'accept_purchase_order' => 'Accept Purchase Order', + 'round_to_seconds' => 'Round To Seconds', + 'activity_142' => 'Quote :number reminder 1 sent', + 'activity_143' => 'Auto Bill succeeded for invoice :invoice', + 'activity_144' => 'Auto Bill failed for invoice :invoice. :notes', + 'activity_145' => 'EInvoice :invoice for :client was e-delivered. :notes', + 'payment_failed' => 'Payment Failed', + 'ssl_host_override' => 'SSL Host Override', + 'upload_logo_short' => 'Upload Logo', ); -return $lang; \ No newline at end of file +return $lang; diff --git a/openapi/api-docs.yaml b/openapi/api-docs.yaml index a736d98853be..e17f3b3db280 100644 --- a/openapi/api-docs.yaml +++ b/openapi/api-docs.yaml @@ -8630,20 +8630,21 @@ paths: default: $ref: "#/components/responses/default" - /api/v1/credits: + /api/v1/vendors: get: tags: - - credits - summary: "List credits" - description: "Lists credits, search and filters allow fine grained lists to be generated.\n *\n * Query parameters can be added to performed more fine grained filtering of the credits, these are handled by the CreditFilters class which defines the methods available" - operationId: getCredits + - vendors + summary: "List vendors" + description: "Lists vendors, search and filters allow fine grained lists to be generated.\n\n Query parameters can be added to performed more fine grained filtering of the vendors, these are handled by the VendorFilters class which defines the methods available" + operationId: getVendors parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" + - $ref: "#/components/parameters/index" responses: 200: - description: "A list of credits" + description: "A list of vendors" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8659,7 +8660,7 @@ paths: data: type: array items: - $ref: '#/components/schemas/Credit' + $ref: '#/components/schemas/Vendor' meta: type: object $ref: '#/components/schemas/Meta' @@ -8677,17 +8678,17 @@ paths: $ref: "#/components/responses/default" post: tags: - - credits - summary: "Create credit" - description: "Adds an credit to the system" - operationId: storeCredit + - vendors + summary: "Create vendor" + description: "Adds a vendor to a company" + operationId: storeVendor parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" responses: 200: - description: "Returns the saved credit object" + description: "Returns the saved clivendorent object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8698,12 +8699,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Credit" + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8712,21 +8712,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - - "/api/v1/credits/{id}": + "/api/v1/vendors/{id}": get: tags: - - credits - summary: "Show credit" - description: "Displays an credit by id" - operationId: showCredit + - vendors + summary: "Show vendor" + description: "Displays a vendor by id" + operationId: showVendor parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Credit Hashed ID" + description: "The vendor Hashed ID" required: true schema: type: string @@ -8734,7 +8733,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the credit object" + description: "Returns the vendor object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8745,12 +8744,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Credit" + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8761,17 +8759,17 @@ paths: $ref: "#/components/responses/default" put: tags: - - Credits - summary: "Update credit" - description: "Handles the updating of an Credit by id" - operationId: updateCredit + - vendors + summary: "Update vendor" + description: "Handles the updating of a vendor by id" + operationId: updateVendor parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Credit Hashed ID" + description: "The Vendor Hashed ID" required: true schema: type: string @@ -8779,7 +8777,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the Credit object" + description: "Returns the vendor object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8790,12 +8788,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Credit" + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8806,17 +8803,17 @@ paths: $ref: "#/components/responses/default" delete: tags: - - credits - summary: "Delete credit" - description: "Handles the deletion of an credit by id" - operationId: deleteCredit + - vendors + summary: "Delete vendor" + description: "Handles the deletion of a vendor by id" + operationId: deleteVendor parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Credit Hashed ID" + description: "The Vendor Hashed ID" required: true schema: type: string @@ -8836,7 +8833,6 @@ paths: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8845,20 +8841,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - "/api/v1/credits/{id}/edit": + "/api/v1/vendors/{id}/edit": get: tags: - - credits - summary: "Edit credit" - description: "Displays an credit by id" - operationId: editCredit + - vendors + summary: "Edit vendor" + description: "Displays a vendor by id" + operationId: editVendor parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Invoice Hashed ID" + description: "The Vendor Hashed ID" required: true schema: type: string @@ -8866,7 +8862,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the credit object" + description: "Returns the vendor object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8877,12 +8873,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Invoice" + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8891,20 +8886,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/credits/create: + /api/v1/vendors/create: get: tags: - - credits - summary: "Blank credit" - description: "Returns a blank object with default values" - operationId: getCreditsCreate + - vendors + summary: "Blank vendor" + description: "Returns a blank vendor with default values" + operationId: getVendorsCreate parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" responses: 200: - description: "A blank credit object" + description: "A blank vendor object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8915,12 +8910,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Credit" + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8929,13 +8923,13 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/credits/bulk: + /api/v1/vendors/bulk: post: tags: - - credits - summary: "Bulk credit actions" + - vendors + summary: "Bulk vendor actions" description: "" - operationId: bulkCredits + operationId: bulkVendors parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" @@ -8949,11 +8943,11 @@ paths: type: array items: description: "Array of hashed IDs to be bulk 'actioned" - type: string - example: '[D2J234DFA,D2J234DFA,D2J234DFA]' + type: integer + example: "[0,1,2,3]" responses: 200: - description: "The Bulk Action response" + description: "The Vendor User response" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -8961,11 +8955,14 @@ paths: $ref: "#/components/headers/X-RateLimit-Remaining" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: $ref: "#/components/responses/403" - 422: $ref: '#/components/responses/422' 429: @@ -8974,62 +8971,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - "/api/v1/credit/{invitation_key}/download": - get: - tags: - - quotes - summary: "Download quote PDF" - description: "Downloads a specific quote" - operationId: downloadCredit - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: invitation_key - in: path - description: "The Credit Invitation Key" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the credit pdf" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/credits/{id}/upload": + "/api/v1/vendors/{id}/upload": post: tags: - - credits - summary: "Upload a credit document" - description: "Handles the uploading of a document to a credit" - operationId: uploadCredits + - vendors + summary: "Uploads a vendor document" + description: "Handles the uploading of a document to a vendor" + operationId: uploadVendor parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Credit Hashed ID" + description: "The Vendor Hashed ID" required: true schema: type: string @@ -9054,7 +9009,7 @@ paths: format: binary responses: 200: - description: "Returns the Credit object" + description: "Returns the Vendor object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9065,7 +9020,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Credit" + $ref: "#/components/schemas/Vendor" 401: $ref: "#/components/responses/401" 403: @@ -9078,6 +9033,557 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" + /api/v1/products: + get: + tags: + - products + summary: "List products" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X GET 'https://invoicing.co/api/v1/products?filter=search&per_page=20&page=1&include=documents' \ + -H "X-API-TOKEN:company-token-test" \ + -H "X-Requested-With: XMLHttpRequest"; + - lang: go + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $products = $ninja->products->all([ + 'filter' => 'search', + 'per_page' => 20, + 'page' => 1, + 'include' => 'documents' + ]); + description: | + Lists products within your company. + + You can search and filter the result set using query parameters. These can be chained together allowing fine grained lists to be generated. + + operationId: getProducts + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - $ref: "#/components/parameters/status" + - $ref: "#/components/parameters/client_id" + - $ref: "#/components/parameters/created_at" + - $ref: "#/components/parameters/updated_at" + - $ref: "#/components/parameters/is_deleted" + - $ref: "#/components/parameters/filter_deleted_clients" + - $ref: "#/components/parameters/vendor_id" + - name: filter + in: query + description: Filter by product name + required: false + schema: + type: string + example: bob + - name: product_key + in: query + description: Filter by product key + required: false + schema: + type: string + example: bob + - name: sort + in: query + description: Returns the list sorted by column in ascending or descending order. + required: false + schema: + type: string + example: id|desc product_key|desc + responses: + 200: + description: "A list of products" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Product' + meta: + type: object + $ref: '#/components/schemas/Meta' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + post: + tags: + - products + summary: "Create Product" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X POST 'https://invoicing.co/api/v1/products' \ + -H "X-API-TOKEN:company-token-test" \ + -H "Content-Type:application/json" \ + -d '{"product_key":"sku_1","notes":"product description","cost":1,"price":10}' \ + -H "X-Requested-With: XMLHttpRequest"; + - lang: go + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $products = $ninja->products->create([ + 'product_key' => "sku_1", + 'notes' => "product description", + 'cost' => 1, + 'price' => 10 + ]); + description: "Adds a product to a company" + operationId: storeProduct + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + requestBody: + description: Product object that needs to be added to the company + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProductRequest' + responses: + 200: + description: "Returns the saved product object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + "/api/v1/products/{id}": + get: + tags: + - products + summary: "Show product" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X GET 'https://invoicing.co/api/v1/products/{id}' \ + -H "X-API-TOKEN:company-token-test" \ + -H "X-Requested-With: XMLHttpRequest"; + - lang: php + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $product = $ninja->products->get("{id}"); + description: "Displays a product by id" + operationId: showProduct + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Product Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the product object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + put: + tags: + - products + summary: "Update product" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X PUT 'https://invoicing.co/api/v1/products/{id}' \ + -H "X-API-TOKEN:company-token-test" \ + -H "Content-Type: application/json" \ + -d '{ + "product_key": "Updated Product", + "price": 150.0, + "notes": "An updated description of the product" + }' + - lang: go + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $product = $ninja->products->update("id", [ + "name" => "Updated Product", + "price" => 150.0, + "description" => "An updated description of the product" + ]); + description: "Handles the updating of a product by id" + operationId: updateProduct + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Product Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + requestBody: + description: Product object that needs to be added to the company + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProductRequest' + responses: + 200: + description: "Returns the Product object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + delete: + tags: + - products + summary: "Delete product" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X DELETE 'https://invoicing.co/api/v1/products/{id}' \ + -H "X-API-TOKEN:company-token-test" \ + -H "X-Requested-With: XMLHttpRequest"; + - lang: go + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $ninja->products->bulk("delete", "id"); + description: "Handles the deletion of a product by id" + operationId: deleteProduct + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Product Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns a HTTP status" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + "/api/v1/products/{id}/edit": + get: + tags: + - products + summary: "Edit product" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X GET 'https://invoicing.co/api/v1/products/{id}/edit' \ + -H "X-API-TOKEN:company-token-test" \ + -H "X-Requested-With: XMLHttpRequest"; + - lang: php + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $product = $ninja->products->get("{id}"); + description: "Displays an Product by id" + operationId: editProduct + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Product Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the Product object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + "/api/v1/products/create": + get: + tags: + - products + summary: "Blank product" + description: "Returns a blank product object with default values" + operationId: getProductsCreate + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + responses: + 200: + description: "A blank Product object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + + /api/v1/products/bulk: + post: + tags: + - products + summary: "Bulk product actions" + x-code-samples: + - lang: curl + label: Curl + source: | + curl -X GET 'https://invoicing.co/api/v1/products/bulk' \ + -H "Content-Type:application/json" \ + -d '{"action":"archive","ids":["id","id2"]}' \ + -H "X-API-TOKEN:company-token-test" \ + -H "X-Requested-With: XMLHttpRequest"; + - lang: php + label: PHP + source: | + $ninja = new InvoiceNinja("your_token"); + $product = $ninja->products->bulk("action", ["id","id2"]); + description: "Archive / Restore / Delete / Set tax id in bulk" + operationId: bulkProducts + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/index" + requestBody: + description: 'Bulk action array' + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProductBulkAction' + responses: + 200: + description: "The Product response" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + + "/api/v1/products/{id}/upload": + post: + tags: + - products + summary: "Add product document" + description: "Handles the uploading of a document to a product" + operationId: uploadProduct + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/client_include" + - name: id + in: path + description: "The Product Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + _method: + type: string + example: POST + documents: + type: array + items: + format: binary + responses: + 200: + description: "Returns the Product object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Product" + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + /api/v1/recurring_invoices: get: tags: @@ -9640,21 +10146,774 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/tasks: + /api/v1/clients: get: tags: - - tasks - summary: "List tasks" - description: "Lists tasks, search and filters allow fine grained lists to be generated.\n *\n * Query parameters can be added to performed more fine grained filtering of the tasks, these are handled by the TaskFilters class which defines the methods available" - operationId: getTasks + - clients + summary: 'List clients' + x-code-samples: + - lang: go + label: php + source: | + $ninja = new InvoiceNinja("your_token"); + $invoices = $ninja->clients->all(); + x-custom-element: + type: markdown + value: | + ### Custom Response Description + This is a custom description for the response returned by the `/example` endpoint. + description: | + When retrieving a list of clients you can also chain query parameters in order to filter the dataset that is returned. For example, you can send a request to the following URL to retrieve clients that have a balance greater than 1000:\ + + ``` + /api/v1/clients?balance=gt:1000 + ``` + + You can also sort the results by adding a sort parameter. The following example will sort the results by the client name in descending order:\ + + ``` + /api/v1/clients?sort=name|desc + ``` + + You can also combine multiple filters together. The following example will return clients that have a balance greater than 1000 and are not deleted and have a name that starts with "Bob":\ + + ``` + /api/v1/clients?balance=gt:1000&name=Bob* + ``` + + If you wish to retrieve child relations, you can also combine the query parameter `?include=` with a comma separated list of relationships:\ + + ``` + /api/v1/clients?include=activities,ledger,system_logs' + ``` + + The per_page and page variables allow pagination of the list of clients. The following example will return the second page of clients with 15 clients per page:\ + + ``` + /api/v1/clients?per_page=15&page=2 + ``` + + The default per_page value is 20. + + operationId: getClients + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + - $ref: '#/components/parameters/index' + - $ref: "#/components/parameters/status" + - $ref: "#/components/parameters/created_at" + - $ref: "#/components/parameters/updated_at" + - $ref: "#/components/parameters/is_deleted" + - $ref: "#/components/parameters/filter_deleted_clients" + - $ref: "#/components/parameters/vendor_id" + - name: name + in: query + description: Filter by client name + required: false + schema: + type: string + example: bob + - name: balance + in: query + description: Filter by client balance, format uses an operator and value separated by a colon. lt,lte, gt, gte, eq + required: false + schema: + type: string + example: lt:10 + - name: between_balance + in: query + description: Filter between client balances, format uses two values separated by a colon + required: false + schema: + type: string + example: 10:100 + - name: email + in: query + description: Filter by client email + required: false + schema: + type: string + example: bob@gmail.com + - name: id_number + in: query + description: Filter by client id_number + required: false + schema: + type: string + example: a1039883 + - name: number + in: query + description: Filter by client number + required: false + schema: + type: string + example: a1039883 + - name: filter + in: query + description: Filters clients on columns - name, id_number, contact.first_name contact.last_name, contact.email, custom_value1-4 + required: false + schema: + type: string + example: a1039883 + - name: sort + in: query + description: Returns the list sorted by column in ascending or descending order. + required: false + schema: + type: string + example: id|desc name|desc balance|asc + + responses: + 200: + description: 'A list of clients' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Client' + example: + $ref: '#/components/schemas/Client' + meta: + type: object + $ref: '#/components/schemas/Meta' + example: + $ref: '#/components/schemas/Meta' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + post: + tags: + - clients + summary: 'Create client' + description: | + Adds a client to a company + + When creating (or updating) a client you must include the child contacts with all mutating requests. Client contacts cannot be modified in isolation. + + operationId: storeClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + requestBody: + description: Client object that needs to be added to the company + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ClientRequest' + responses: + 200: + description: 'Returns the saved client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + '/api/v1/clients/{id}': + get: + tags: + - clients + summary: 'Show client' + description: 'Displays a client by id' + operationId: showClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + put: + tags: + - clients + summary: 'Update client' + description: 'Handles the updating of a client by id' + operationId: updateClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + requestBody: + description: Client object that needs to be updated + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ClientRequest' + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + delete: + tags: + - clients + summary: 'Delete client' + description: 'Handles the deletion of a client by id' + operationId: deleteClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: 'Returns a HTTP status' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + '/api/v1/clients/{id}/edit': + get: + tags: + - clients + summary: 'Edit Client' + description: 'Displays a client by id, essentially an alias of the show route' + operationId: editClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/include' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + /api/v1/clients/create: + get: + tags: + - clients + summary: 'Blank Client' + description: 'Returns a blank object with default values' + operationId: getClientsCreate + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + responses: + 200: + description: 'A blank client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + /api/v1/clients/bulk: + post: + tags: + - clients + summary: 'Bulk client actions' + description: 'Archive / Restore / Delete in bulk' + operationId: bulkClients + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/index' + requestBody: + description: 'Bulk action array' + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GenericBulkAction' + + responses: + 200: + description: 'The Client listresponse' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + '/api/v1/clients/{id}/upload': + post: + tags: + - clients + summary: 'Add client document' + description: 'Handles the uploading of a document to a client, please note due to a quirk in REST you will need to use a _method parameter with value of POST' + operationId: uploadClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + _method: + type: string + example: POST + documents: + type: array + items: + format: binary + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + '/api/v1/clients/{id}/purge': + post: + tags: + - clients + summary: 'Purge client' + description: | + Handles purging a clients. + + Please note this is a destructive action. + + This action will remove all data associated with the client and cannot be undone. + operationId: purgeClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/X-API-PASSWORD' + - $ref: '#/components/parameters/client_include' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + '/api/v1/clients/{id}/{mergeable_client_hashed_id}/merge': + post: + tags: + - clients + summary: 'Merge client' + description: | + Handles merging 2 clients + + The id parameter is the client that will be the primary client after the merge has completed. + + The mergeable_client_hashed_id is the client that will be merged into the primary client, this clients records will be updated and associated with the primary client. + operationId: mergeClient + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/client_include' + - $ref: '#/components/parameters/X-API-PASSWORD' + - name: id + in: path + description: 'The Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + - name: mergeable_client_hashed_id + in: path + description: 'The Mergeable Client Hashed ID' + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + default: + $ref: '#/components/responses/default' + /api/v1/client_statement: + post: + tags: + - clients + summary: 'Client statement PDF' + description: 'Return a PDF of the client statement' + operationId: clientStatement + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/include' + requestBody: + description: 'Statement Options' + required: true + content: + application/json: + schema: + properties: + start_date: + description: 'The start date of the statement period - format Y-m-d' + type: string + end_date: + description: 'The start date of the statement period - format Y-m-d' + type: string + client_id: + description: 'The hashed ID of the client' + type: string + show_payments_table: + description: 'Flag which determines if the payments table is shown' + type: boolean + show_credits_table: + description: 'Flag which determines if the credits table is shown' + type: boolean + show_aging_table: + description: 'Flag which determines if the aging table is shown' + type: boolean + type: object + responses: + 200: + description: 'Returns the client object' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + content: + application/json: + schema: + $ref: '#/components/schemas/Client' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + /api/v1/reactivate_email/{bounce_id}: + post: + tags: + - clients + summary: 'Removes email suppression of a user in the system' + description: 'Emails are suppressed by PostMark, when they receive a Hard bounce / Spam Complaint. This endpoint allows you to remove the suppression and send emails to the user again.' + operationId: reactivateEmail + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/include' + - name: bounce_id + in: path + description: 'The postmark Bounce ID reference' + required: true + schema: + type: string + format: string + example: 123243 + responses: + 200: + description: 'Success' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + 400: + description: 'Postmark exception - generated if the suppression cannot be removed for any reason' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + /api/v1/clients/{client}/updateTaxData: + post: + tags: + - clients + summary: 'Update tax data' + description: 'Updates the clients tax data - if their address has changed' + operationId: updateClientTaxData + parameters: + - $ref: '#/components/parameters/X-API-TOKEN' + - $ref: '#/components/parameters/X-Requested-With' + - $ref: '#/components/parameters/include' + - name: client + in: path + description: 'The Client Hashed ID reference' + required: true + schema: + type: string + format: string + example: V2J234DFA + responses: + 200: + description: 'Success' + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' + X-RateLimit-Remaining: + $ref: '#/components/headers/X-RateLimit-Remaining' + X-RateLimit-Limit: + $ref: '#/components/headers/X-RateLimit-Limit' + 400: + description: 'Postmark exception - generated if the suppression cannot be removed for any reason' + 401: + $ref: '#/components/responses/401' + 403: + $ref: '#/components/responses/403' + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: '#/components/responses/default' + /api/v1/credits: + get: + tags: + - credits + summary: "List credits" + description: "Lists credits, search and filters allow fine grained lists to be generated.\n *\n * Query parameters can be added to performed more fine grained filtering of the credits, these are handled by the CreditFilters class which defines the methods available" + operationId: getCredits parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - - $ref: "#/components/parameters/index" responses: 200: - description: "A list of tasks" + description: "A list of credits" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9670,7 +10929,7 @@ paths: data: type: array items: - $ref: '#/components/schemas/Task' + $ref: '#/components/schemas/Credit' meta: type: object $ref: '#/components/schemas/Meta' @@ -9688,17 +10947,17 @@ paths: $ref: "#/components/responses/default" post: tags: - - tasks - summary: "Create task" - description: "Adds an task to a company" - operationId: storeTask + - credits + summary: "Create credit" + description: "Adds an credit to the system" + operationId: storeCredit parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" responses: 200: - description: "Returns the saved task object" + description: "Returns the saved credit object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9709,7 +10968,374 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" + $ref: "#/components/schemas/Credit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + + "/api/v1/credits/{id}": + get: + tags: + - credits + summary: "Show credit" + description: "Displays an credit by id" + operationId: showCredit + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Credit Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the credit object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Credit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + put: + tags: + - Credits + summary: "Update credit" + description: "Handles the updating of an Credit by id" + operationId: updateCredit + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Credit Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the Credit object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Credit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + delete: + tags: + - credits + summary: "Delete credit" + description: "Handles the deletion of an credit by id" + operationId: deleteCredit + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Credit Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns a HTTP status" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + "/api/v1/credits/{id}/edit": + get: + tags: + - credits + summary: "Edit credit" + description: "Displays an credit by id" + operationId: editCredit + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Invoice Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the credit object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Invoice" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + /api/v1/credits/create: + get: + tags: + - credits + summary: "Blank credit" + description: "Returns a blank object with default values" + operationId: getCreditsCreate + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + responses: + 200: + description: "A blank credit object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Credit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + /api/v1/credits/bulk: + post: + tags: + - credits + summary: "Bulk credit actions" + description: "" + operationId: bulkCredits + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/index" + requestBody: + description: "User credentials" + required: true + content: + application/json: + schema: + type: array + items: + description: "Array of hashed IDs to be bulk 'actioned" + type: string + example: '[D2J234DFA,D2J234DFA,D2J234DFA]' + responses: + 200: + description: "The Bulk Action response" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + "/api/v1/credit/{invitation_key}/download": + get: + tags: + - quotes + summary: "Download quote PDF" + description: "Downloads a specific quote" + operationId: downloadCredit + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: invitation_key + in: path + description: "The Credit Invitation Key" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the credit pdf" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + "/api/v1/credits/{id}/upload": + post: + tags: + - credits + summary: "Upload a credit document" + description: "Handles the uploading of a document to a credit" + operationId: uploadCredits + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Credit Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + requestBody: + description: "File Upload Body" + required: true + content: + multipart/form-data: + schema: + type: object + properties: + _method: + type: string + example: PUT + documents: + type: array + items: + description: "Array of binary documents for upload" + type: string + format: binary + responses: + 200: + description: "Returns the Credit object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Credit" 401: $ref: "#/components/responses/401" 403: @@ -9722,28 +11348,65 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - "/api/v1/tasks/{id}": + /api/v1/projects: get: tags: - - tasks - summary: "Show task" - description: "Displays a task by id" - operationId: showTask + - projects + summary: "List projects" + description: "Lists projects" + operationId: getProjects parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Task Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA + - $ref: "#/components/parameters/index" responses: 200: - description: "Returns the task object" + description: "A list of projects" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Project' + meta: + type: object + $ref: '#/components/schemas/Meta' + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + post: + tags: + - projects + summary: "Create project" + description: "Adds an project to a company" + operationId: storeProject + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + responses: + 200: + description: "Returns the saved project object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9754,7 +11417,52 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" + $ref: "#/components/schemas/Project" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" + "/api/v1/projects/{id}": + get: + tags: + - projects + summary: "Show project" + description: "Displays a project by id" + operationId: showProject + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + - name: id + in: path + description: "The Project Hashed ID" + required: true + schema: + type: string + format: string + example: D2J234DFA + responses: + 200: + description: "Returns the expense object" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + content: + application/json: + schema: + $ref: "#/components/schemas/Project" 401: $ref: "#/components/responses/401" 403: @@ -9769,17 +11477,17 @@ paths: $ref: "#/components/responses/default" put: tags: - - tasks - summary: "Update task" - description: "Handles the updating of a task by id" - operationId: updateTask + - projects + summary: "Update project" + description: "Handles the updating of a project by id" + operationId: updateProject parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The task Hashed ID" + description: "The Project Hashed ID" required: true schema: type: string @@ -9787,7 +11495,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the task object" + description: "Returns the project object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9798,7 +11506,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" + $ref: "#/components/schemas/Project" 401: $ref: "#/components/responses/401" 403: @@ -9813,17 +11521,17 @@ paths: $ref: "#/components/responses/default" delete: tags: - - tasks - summary: "Delete task" - description: "Handles the deletion of a task by id" - operationId: deleteTask + - projects + summary: "Delete project" + description: "Handles the deletion of a project by id" + operationId: deleteProject parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Task Hashed ID" + description: "The Project Hashed ID" required: true schema: type: string @@ -9851,20 +11559,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - "/api/v1/tasks/{id}/edit": + "/api/v1/projects/{id}/edit": get: tags: - - tasks - summary: "Edit task" - description: "Displays a task by id" - operationId: editTask + - projects + summary: "Edit project" + description: "Displays a project by id" + operationId: editProject parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Task Hashed ID" + description: "The Project Hashed ID" required: true schema: type: string @@ -9872,7 +11580,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the client object" + description: "Returns the project object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9883,7 +11591,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" + $ref: "#/components/schemas/Project" 401: $ref: "#/components/responses/401" 403: @@ -9896,20 +11604,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/tasks/create: + /api/v1/projects/create: get: tags: - - tasks - summary: "Blank task" - description: "Returns a blank task with default values" - operationId: getTasksCreate + - projects + summary: "Blank project" + description: "Returns a blank object with default values" + operationId: getProjectsCreate parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" responses: 200: - description: "A blank task object" + description: "A blank project object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9920,7 +11628,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" + $ref: "#/components/schemas/Project" 401: $ref: "#/components/responses/401" 403: @@ -9933,13 +11641,13 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/tasks/bulk: + /api/v1/projects/bulk: post: tags: - - tasks - summary: "Bulk task actions" + - projects + summary: "Bulk project actions" description: "" - operationId: bulkTasks + operationId: bulkProjects parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" @@ -9957,7 +11665,7 @@ paths: example: "[0,1,2,3]" responses: 200: - description: "The Task User response" + description: "The Project User response" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -9968,7 +11676,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" + $ref: "#/components/schemas/Project" 401: $ref: "#/components/responses/401" 403: @@ -9981,20 +11689,20 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - "/api/v1/tasks/{id}/upload": + "/api/v1/projects/{id}/upload": post: tags: - - tasks - summary: "Uploads a task document" - description: "Handles the uploading of a document to a task" - operationId: uploadTask + - projects + summary: "Uploads a project document" + description: "Handles the uploading of a document to a project" + operationId: uploadProject parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Task Hashed ID" + description: "The Project Hashed ID" required: true schema: type: string @@ -10019,7 +11727,7 @@ paths: format: binary responses: 200: - description: "Returns the Task object" + description: "Returns the Project object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -10030,40 +11738,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Task" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - /api/v1/tasks/sort: - post: - tags: - - tasks - summary: "Sort tasks on KanBan" - description: "Sorts tasks after drag and drop on the KanBan." - operationId: sortTasks - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - responses: - 200: - description: "Returns an Ok, 200 HTTP status" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" + $ref: "#/components/schemas/Project" 401: $ref: "#/components/responses/401" 403: @@ -11333,812 +13008,6 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/projects: - get: - tags: - - projects - summary: "List projects" - description: "Lists projects" - operationId: getProjects - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - $ref: "#/components/parameters/index" - responses: - 200: - description: "A list of projects" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Project' - meta: - type: object - $ref: '#/components/schemas/Meta' - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - post: - tags: - - projects - summary: "Create project" - description: "Adds an project to a company" - operationId: storeProject - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - responses: - 200: - description: "Returns the saved project object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/projects/{id}": - get: - tags: - - projects - summary: "Show project" - description: "Displays a project by id" - operationId: showProject - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Project Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the expense object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - put: - tags: - - projects - summary: "Update project" - description: "Handles the updating of a project by id" - operationId: updateProject - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Project Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the project object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - delete: - tags: - - projects - summary: "Delete project" - description: "Handles the deletion of a project by id" - operationId: deleteProject - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Project Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns a HTTP status" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/projects/{id}/edit": - get: - tags: - - projects - summary: "Edit project" - description: "Displays a project by id" - operationId: editProject - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Project Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the project object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - /api/v1/projects/create: - get: - tags: - - projects - summary: "Blank project" - description: "Returns a blank object with default values" - operationId: getProjectsCreate - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - responses: - 200: - description: "A blank project object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - /api/v1/projects/bulk: - post: - tags: - - projects - summary: "Bulk project actions" - description: "" - operationId: bulkProjects - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/index" - requestBody: - description: "User credentials" - required: true - content: - application/json: - schema: - type: array - items: - description: "Array of hashed IDs to be bulk 'actioned" - type: integer - example: "[0,1,2,3]" - responses: - 200: - description: "The Project User response" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/projects/{id}/upload": - post: - tags: - - projects - summary: "Uploads a project document" - description: "Handles the uploading of a document to a project" - operationId: uploadProject - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Project Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - requestBody: - description: "File Upload Body" - required: true - content: - multipart/form-data: - schema: - type: object - properties: - _method: - type: string - example: PUT - documents: - type: array - items: - description: "Array of binary documents for upload" - type: string - format: binary - responses: - 200: - description: "Returns the Project object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Project" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - /api/v1/vendors: - get: - tags: - - vendors - summary: "List vendors" - description: "Lists vendors, search and filters allow fine grained lists to be generated.\n\n Query parameters can be added to performed more fine grained filtering of the vendors, these are handled by the VendorFilters class which defines the methods available" - operationId: getVendors - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - $ref: "#/components/parameters/index" - responses: - 200: - description: "A list of vendors" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Vendor' - meta: - type: object - $ref: '#/components/schemas/Meta' - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - post: - tags: - - vendors - summary: "Create vendor" - description: "Adds a vendor to a company" - operationId: storeVendor - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - responses: - 200: - description: "Returns the saved clivendorent object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/vendors/{id}": - get: - tags: - - vendors - summary: "Show vendor" - description: "Displays a vendor by id" - operationId: showVendor - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The vendor Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the vendor object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - put: - tags: - - vendors - summary: "Update vendor" - description: "Handles the updating of a vendor by id" - operationId: updateVendor - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Vendor Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the vendor object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - delete: - tags: - - vendors - summary: "Delete vendor" - description: "Handles the deletion of a vendor by id" - operationId: deleteVendor - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Vendor Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns a HTTP status" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/vendors/{id}/edit": - get: - tags: - - vendors - summary: "Edit vendor" - description: "Displays a vendor by id" - operationId: editVendor - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Vendor Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: "Returns the vendor object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - /api/v1/vendors/create: - get: - tags: - - vendors - summary: "Blank vendor" - description: "Returns a blank vendor with default values" - operationId: getVendorsCreate - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - responses: - 200: - description: "A blank vendor object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - /api/v1/vendors/bulk: - post: - tags: - - vendors - summary: "Bulk vendor actions" - description: "" - operationId: bulkVendors - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/index" - requestBody: - description: "User credentials" - required: true - content: - application/json: - schema: - type: array - items: - description: "Array of hashed IDs to be bulk 'actioned" - type: integer - example: "[0,1,2,3]" - responses: - 200: - description: "The Vendor User response" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" - "/api/v1/vendors/{id}/upload": - post: - tags: - - vendors - summary: "Uploads a vendor document" - description: "Handles the uploading of a document to a vendor" - operationId: uploadVendor - parameters: - - $ref: "#/components/parameters/X-API-TOKEN" - - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/include" - - name: id - in: path - description: "The Vendor Hashed ID" - required: true - schema: - type: string - format: string - example: D2J234DFA - requestBody: - description: "File Upload Body" - required: true - content: - multipart/form-data: - schema: - type: object - properties: - _method: - type: string - example: PUT - documents: - type: array - items: - description: "Array of binary documents for upload" - type: string - format: binary - responses: - 200: - description: "Returns the Vendor object" - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" - X-RateLimit-Remaining: - $ref: "#/components/headers/X-RateLimit-Remaining" - X-RateLimit-Limit: - $ref: "#/components/headers/X-RateLimit-Limit" - content: - application/json: - schema: - $ref: "#/components/schemas/Vendor" - 401: - $ref: "#/components/responses/401" - 403: - $ref: "#/components/responses/403" - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: "#/components/responses/default" /api/v1/payments: get: tags: @@ -12684,760 +13553,6 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/clients: - get: - tags: - - clients - summary: 'List clients' - x-code-samples: - - lang: go - label: php - source: | - $ninja = new InvoiceNinja("your_token"); - $invoices = $ninja->clients->all(); - x-custom-element: - type: markdown - value: | - ### Custom Response Description - This is a custom description for the response returned by the `/example` endpoint. - description: | - When retrieving a list of clients you can also chain query parameters in order to filter the dataset that is returned. For example, you can send a request to the following URL to retrieve clients that have a balance greater than 1000:\ - - ``` - /api/v1/clients?balance=gt:1000 - ``` - - You can also sort the results by adding a sort parameter. The following example will sort the results by the client name in descending order:\ - - ``` - /api/v1/clients?sort=name|desc - ``` - - You can also combine multiple filters together. The following example will return clients that have a balance greater than 1000 and are not deleted and have a name that starts with "Bob":\ - - ``` - /api/v1/clients?balance=gt:1000&name=Bob* - ``` - - If you wish to retrieve child relations, you can also combine the query parameter `?include=` with a comma separated list of relationships:\ - - ``` - /api/v1/clients?include=activities,ledger,system_logs' - ``` - - The per_page and page variables allow pagination of the list of clients. The following example will return the second page of clients with 15 clients per page:\ - - ``` - /api/v1/clients?per_page=15&page=2 - ``` - - The default per_page value is 20. - - operationId: getClients - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - - $ref: '#/components/parameters/index' - - $ref: "#/components/parameters/status" - - $ref: "#/components/parameters/created_at" - - $ref: "#/components/parameters/updated_at" - - $ref: "#/components/parameters/is_deleted" - - $ref: "#/components/parameters/filter_deleted_clients" - - $ref: "#/components/parameters/vendor_id" - - name: name - in: query - description: Filter by client name - required: false - schema: - type: string - example: bob - - name: balance - in: query - description: Filter by client balance, format uses an operator and value separated by a colon. lt,lte, gt, gte, eq - required: false - schema: - type: string - example: lt:10 - - name: between_balance - in: query - description: Filter between client balances, format uses two values separated by a colon - required: false - schema: - type: string - example: 10:100 - - name: email - in: query - description: Filter by client email - required: false - schema: - type: string - example: bob@gmail.com - - name: id_number - in: query - description: Filter by client id_number - required: false - schema: - type: string - example: a1039883 - - name: number - in: query - description: Filter by client number - required: false - schema: - type: string - example: a1039883 - - name: filter - in: query - description: Filters clients on columns - name, id_number, contact.first_name contact.last_name, contact.email, custom_value1-4 - required: false - schema: - type: string - example: a1039883 - - name: sort - in: query - description: Returns the list sorted by column in ascending or descending order. - required: false - schema: - type: string - example: id|desc name|desc balance|asc - - responses: - 200: - description: 'A list of clients' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - type: object - properties: - data: - type: array - items: - $ref: '#/components/schemas/Client' - example: - $ref: '#/components/schemas/Client' - meta: - type: object - $ref: '#/components/schemas/Meta' - example: - $ref: '#/components/schemas/Meta' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - post: - tags: - - clients - summary: 'Create client' - description: | - Adds a client to a company - - When creating (or updating) a client you must include the child contacts with all mutating requests. Client contacts cannot be modified in isolation. - - operationId: storeClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - requestBody: - description: Client object that needs to be added to the company - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ClientRequest' - responses: - 200: - description: 'Returns the saved client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - '/api/v1/clients/{id}': - get: - tags: - - clients - summary: 'Show client' - description: 'Displays a client by id' - operationId: showClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - put: - tags: - - clients - summary: 'Update client' - description: 'Handles the updating of a client by id' - operationId: updateClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - requestBody: - description: Client object that needs to be updated - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ClientRequest' - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - delete: - tags: - - clients - summary: 'Delete client' - description: 'Handles the deletion of a client by id' - operationId: deleteClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: 'Returns a HTTP status' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - '/api/v1/clients/{id}/edit': - get: - tags: - - clients - summary: 'Edit Client' - description: 'Displays a client by id, essentially an alias of the show route' - operationId: editClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/include' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - /api/v1/clients/create: - get: - tags: - - clients - summary: 'Blank Client' - description: 'Returns a blank object with default values' - operationId: getClientsCreate - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - responses: - 200: - description: 'A blank client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - /api/v1/clients/bulk: - post: - tags: - - clients - summary: 'Bulk client actions' - description: 'Archive / Restore / Delete in bulk' - operationId: bulkClients - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/index' - requestBody: - description: 'Bulk action array' - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/GenericBulkAction' - - responses: - 200: - description: 'The Client listresponse' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - '/api/v1/clients/{id}/upload': - post: - tags: - - clients - summary: 'Add client document' - description: 'Handles the uploading of a document to a client, please note due to a quirk in REST you will need to use a _method parameter with value of POST' - operationId: uploadClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - _method: - type: string - example: POST - documents: - type: array - items: - format: binary - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - '/api/v1/clients/{id}/purge': - post: - tags: - - clients - summary: 'Purge client' - description: | - Handles purging a clients. - - Please note this is a destructive action. - - This action will remove all data associated with the client and cannot be undone. - operationId: purgeClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/X-API-PASSWORD' - - $ref: '#/components/parameters/client_include' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - '/api/v1/clients/{id}/{mergeable_client_hashed_id}/merge': - post: - tags: - - clients - summary: 'Merge client' - description: | - Handles merging 2 clients - - The id parameter is the client that will be the primary client after the merge has completed. - - The mergeable_client_hashed_id is the client that will be merged into the primary client, this clients records will be updated and associated with the primary client. - operationId: mergeClient - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/client_include' - - $ref: '#/components/parameters/X-API-PASSWORD' - - name: id - in: path - description: 'The Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - - name: mergeable_client_hashed_id - in: path - description: 'The Mergeable Client Hashed ID' - required: true - schema: - type: string - format: string - example: D2J234DFA - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - default: - $ref: '#/components/responses/default' - /api/v1/client_statement: - post: - tags: - - clients - summary: 'Client statement PDF' - description: 'Return a PDF of the client statement' - operationId: clientStatement - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/include' - requestBody: - description: 'Statement Options' - required: true - content: - application/json: - schema: - properties: - start_date: - description: 'The start date of the statement period - format Y-m-d' - type: string - end_date: - description: 'The start date of the statement period - format Y-m-d' - type: string - client_id: - description: 'The hashed ID of the client' - type: string - show_payments_table: - description: 'Flag which determines if the payments table is shown' - type: boolean - show_credits_table: - description: 'Flag which determines if the credits table is shown' - type: boolean - show_aging_table: - description: 'Flag which determines if the aging table is shown' - type: boolean - type: object - responses: - 200: - description: 'Returns the client object' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - content: - application/json: - schema: - $ref: '#/components/schemas/Client' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: '#/components/responses/default' - /api/v1/reactivate_email/{bounce_id}: - post: - tags: - - clients - summary: 'Removes email suppression of a user in the system' - description: 'Emails are suppressed by PostMark, when they receive a Hard bounce / Spam Complaint. This endpoint allows you to remove the suppression and send emails to the user again.' - operationId: reactivateEmail - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/include' - - name: bounce_id - in: path - description: 'The postmark Bounce ID reference' - required: true - schema: - type: string - format: string - example: 123243 - responses: - 200: - description: 'Success' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - 400: - description: 'Postmark exception - generated if the suppression cannot be removed for any reason' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: '#/components/responses/default' - /api/v1/clients/{client}/updateTaxData: - post: - tags: - - clients - summary: 'Update tax data' - description: 'Updates the clients tax data - if their address has changed' - operationId: updateClientTaxData - parameters: - - $ref: '#/components/parameters/X-API-TOKEN' - - $ref: '#/components/parameters/X-Requested-With' - - $ref: '#/components/parameters/include' - - name: client - in: path - description: 'The Client Hashed ID reference' - required: true - schema: - type: string - format: string - example: V2J234DFA - responses: - 200: - description: 'Success' - headers: - X-MINIMUM-CLIENT-VERSION: - $ref: '#/components/headers/X-MINIMUM-CLIENT-VERSION' - X-RateLimit-Remaining: - $ref: '#/components/headers/X-RateLimit-Remaining' - X-RateLimit-Limit: - $ref: '#/components/headers/X-RateLimit-Limit' - 400: - description: 'Postmark exception - generated if the suppression cannot be removed for any reason' - 401: - $ref: '#/components/responses/401' - 403: - $ref: '#/components/responses/403' - 422: - $ref: '#/components/responses/422' - 429: - $ref: '#/components/responses/429' - 5XX: - description: 'Server error' - default: - $ref: '#/components/responses/default' /api/v1/purchase_orders: get: tags: @@ -13934,69 +14049,21 @@ paths: description: 'Server error' default: $ref: "#/components/responses/default" - /api/v1/products: + /api/v1/tasks: get: tags: - - products - summary: "List products" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X GET 'https://invoicing.co/api/v1/products?filter=search&per_page=20&page=1&include=documents' \ - -H "X-API-TOKEN:company-token-test" \ - -H "X-Requested-With: XMLHttpRequest"; - - lang: go - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $products = $ninja->products->all([ - 'filter' => 'search', - 'per_page' => 20, - 'page' => 1, - 'include' => 'documents' - ]); - description: | - Lists products within your company. - - You can search and filter the result set using query parameters. These can be chained together allowing fine grained lists to be generated. - - operationId: getProducts + - tasks + summary: "List tasks" + description: "Lists tasks, search and filters allow fine grained lists to be generated.\n *\n * Query parameters can be added to performed more fine grained filtering of the tasks, these are handled by the TaskFilters class which defines the methods available" + operationId: getTasks parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - - $ref: "#/components/parameters/status" - - $ref: "#/components/parameters/client_id" - - $ref: "#/components/parameters/created_at" - - $ref: "#/components/parameters/updated_at" - - $ref: "#/components/parameters/is_deleted" - - $ref: "#/components/parameters/filter_deleted_clients" - - $ref: "#/components/parameters/vendor_id" - - name: filter - in: query - description: Filter by product name - required: false - schema: - type: string - example: bob - - name: product_key - in: query - description: Filter by product key - required: false - schema: - type: string - example: bob - - name: sort - in: query - description: Returns the list sorted by column in ascending or descending order. - required: false - schema: - type: string - example: id|desc product_key|desc + - $ref: "#/components/parameters/index" responses: 200: - description: "A list of products" + description: "A list of tasks" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14012,14 +14079,14 @@ paths: data: type: array items: - $ref: '#/components/schemas/Product' + $ref: '#/components/schemas/Task' meta: type: object $ref: '#/components/schemas/Meta' 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14027,46 +14094,20 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' + $ref: "#/components/responses/default" post: tags: - - products - summary: "Create Product" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X POST 'https://invoicing.co/api/v1/products' \ - -H "X-API-TOKEN:company-token-test" \ - -H "Content-Type:application/json" \ - -d '{"product_key":"sku_1","notes":"product description","cost":1,"price":10}' \ - -H "X-Requested-With: XMLHttpRequest"; - - lang: go - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $products = $ninja->products->create([ - 'product_key' => "sku_1", - 'notes' => "product description", - 'cost' => 1, - 'price' => 10 - ]); - description: "Adds a product to a company" - operationId: storeProduct + - tasks + summary: "Create task" + description: "Adds an task to a company" + operationId: storeTask parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - requestBody: - description: Product object that needs to be added to the company - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ProductRequest' responses: 200: - description: "Returns the saved product object" + description: "Returns the saved task object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14077,11 +14118,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14089,33 +14130,21 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' - "/api/v1/products/{id}": + $ref: "#/components/responses/default" + "/api/v1/tasks/{id}": get: tags: - - products - summary: "Show product" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X GET 'https://invoicing.co/api/v1/products/{id}' \ - -H "X-API-TOKEN:company-token-test" \ - -H "X-Requested-With: XMLHttpRequest"; - - lang: php - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $product = $ninja->products->get("{id}"); - description: "Displays a product by id" - operationId: showProduct + - tasks + summary: "Show task" + description: "Displays a task by id" + operationId: showTask parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Product Hashed ID" + description: "The Task Hashed ID" required: true schema: type: string @@ -14123,7 +14152,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the product object" + description: "Returns the task object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14134,11 +14163,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14146,56 +14175,28 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' + $ref: "#/components/responses/default" put: tags: - - products - summary: "Update product" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X PUT 'https://invoicing.co/api/v1/products/{id}' \ - -H "X-API-TOKEN:company-token-test" \ - -H "Content-Type: application/json" \ - -d '{ - "product_key": "Updated Product", - "price": 150.0, - "notes": "An updated description of the product" - }' - - lang: go - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $product = $ninja->products->update("id", [ - "name" => "Updated Product", - "price" => 150.0, - "description" => "An updated description of the product" - ]); - description: "Handles the updating of a product by id" - operationId: updateProduct + - tasks + summary: "Update task" + description: "Handles the updating of a task by id" + operationId: updateTask parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Product Hashed ID" + description: "The task Hashed ID" required: true schema: type: string format: string example: D2J234DFA - requestBody: - description: Product object that needs to be added to the company - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ProductRequest' responses: 200: - description: "Returns the Product object" + description: "Returns the task object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14206,11 +14207,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14218,32 +14219,20 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' + $ref: "#/components/responses/default" delete: tags: - - products - summary: "Delete product" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X DELETE 'https://invoicing.co/api/v1/products/{id}' \ - -H "X-API-TOKEN:company-token-test" \ - -H "X-Requested-With: XMLHttpRequest"; - - lang: go - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $ninja->products->bulk("delete", "id"); - description: "Handles the deletion of a product by id" - operationId: deleteProduct + - tasks + summary: "Delete task" + description: "Handles the deletion of a task by id" + operationId: deleteTask parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Product Hashed ID" + description: "The Task Hashed ID" required: true schema: type: string @@ -14260,9 +14249,9 @@ paths: X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14270,33 +14259,21 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' - "/api/v1/products/{id}/edit": + $ref: "#/components/responses/default" + "/api/v1/tasks/{id}/edit": get: tags: - - products - summary: "Edit product" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X GET 'https://invoicing.co/api/v1/products/{id}/edit' \ - -H "X-API-TOKEN:company-token-test" \ - -H "X-Requested-With: XMLHttpRequest"; - - lang: php - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $product = $ninja->products->get("{id}"); - description: "Displays an Product by id" - operationId: editProduct + - tasks + summary: "Edit task" + description: "Displays a task by id" + operationId: editTask parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" - name: id in: path - description: "The Product Hashed ID" + description: "The Task Hashed ID" required: true schema: type: string @@ -14304,7 +14281,7 @@ paths: example: D2J234DFA responses: 200: - description: "Returns the Product object" + description: "Returns the client object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14315,11 +14292,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14327,21 +14304,21 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' - "/api/v1/products/create": + $ref: "#/components/responses/default" + /api/v1/tasks/create: get: tags: - - products - summary: "Blank product" - description: "Returns a blank product object with default values" - operationId: getProductsCreate + - tasks + summary: "Blank task" + description: "Returns a blank task with default values" + operationId: getTasksCreate parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/include" responses: 200: - description: "A blank Product object" + description: "A blank task object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14352,11 +14329,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14364,43 +14341,32 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' - - /api/v1/products/bulk: + $ref: "#/components/responses/default" + /api/v1/tasks/bulk: post: tags: - - products - summary: "Bulk product actions" - x-code-samples: - - lang: curl - label: Curl - source: | - curl -X GET 'https://invoicing.co/api/v1/products/bulk' \ - -H "Content-Type:application/json" \ - -d '{"action":"archive","ids":["id","id2"]}' \ - -H "X-API-TOKEN:company-token-test" \ - -H "X-Requested-With: XMLHttpRequest"; - - lang: php - label: PHP - source: | - $ninja = new InvoiceNinja("your_token"); - $product = $ninja->products->bulk("action", ["id","id2"]); - description: "Archive / Restore / Delete / Set tax id in bulk" - operationId: bulkProducts + - tasks + summary: "Bulk task actions" + description: "" + operationId: bulkTasks parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - $ref: "#/components/parameters/index" requestBody: - description: 'Bulk action array' + description: "User credentials" required: true content: application/json: schema: - $ref: '#/components/schemas/ProductBulkAction' + type: array + items: + description: "Array of hashed IDs to be bulk 'actioned" + type: integer + example: "[0,1,2,3]" responses: 200: - description: "The Product response" + description: "The Task User response" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14411,11 +14377,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14423,28 +14389,28 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' - - "/api/v1/products/{id}/upload": + $ref: "#/components/responses/default" + "/api/v1/tasks/{id}/upload": post: tags: - - products - summary: "Add product document" - description: "Handles the uploading of a document to a product" - operationId: uploadProduct + - tasks + summary: "Uploads a task document" + description: "Handles the uploading of a document to a task" + operationId: uploadTask parameters: - $ref: "#/components/parameters/X-API-TOKEN" - $ref: "#/components/parameters/X-Requested-With" - - $ref: "#/components/parameters/client_include" + - $ref: "#/components/parameters/include" - name: id in: path - description: "The Product Hashed ID" + description: "The Task Hashed ID" required: true schema: type: string format: string example: D2J234DFA requestBody: + description: "File Upload Body" required: true content: multipart/form-data: @@ -14453,14 +14419,16 @@ paths: properties: _method: type: string - example: POST + example: PUT documents: type: array items: + description: "Array of binary documents for upload" + type: string format: binary responses: 200: - description: "Returns the Product object" + description: "Returns the Task object" headers: X-MINIMUM-CLIENT-VERSION: $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" @@ -14471,11 +14439,11 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/Product" + $ref: "#/components/schemas/Task" 401: - $ref: '#/components/responses/401' + $ref: "#/components/responses/401" 403: - $ref: '#/components/responses/403' + $ref: "#/components/responses/403" 422: $ref: '#/components/responses/422' 429: @@ -14483,8 +14451,40 @@ paths: 5XX: description: 'Server error' default: - $ref: '#/components/responses/default' - + $ref: "#/components/responses/default" + /api/v1/tasks/sort: + post: + tags: + - tasks + summary: "Sort tasks on KanBan" + description: "Sorts tasks after drag and drop on the KanBan." + operationId: sortTasks + parameters: + - $ref: "#/components/parameters/X-API-TOKEN" + - $ref: "#/components/parameters/X-Requested-With" + - $ref: "#/components/parameters/include" + responses: + 200: + description: "Returns an Ok, 200 HTTP status" + headers: + X-MINIMUM-CLIENT-VERSION: + $ref: "#/components/headers/X-MINIMUM-CLIENT-VERSION" + X-RateLimit-Remaining: + $ref: "#/components/headers/X-RateLimit-Remaining" + X-RateLimit-Limit: + $ref: "#/components/headers/X-RateLimit-Limit" + 401: + $ref: "#/components/responses/401" + 403: + $ref: "#/components/responses/403" + 422: + $ref: '#/components/responses/422' + 429: + $ref: '#/components/responses/429' + 5XX: + description: 'Server error' + default: + $ref: "#/components/responses/default" components: headers: X-MINIMUM-CLIENT-VERSION: @@ -14579,6 +14579,19 @@ components: # - prev: null # - next: null responses: + 500: + description: 'Gateway Error' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + 403: + description: 'Authorization error' + content: + application/json: + schema: + $ref: '#/components/schemas/AuthorizationError' + default: description: 'Unexpected Error' content: @@ -14591,31 +14604,24 @@ components: application/json: schema: $ref: '#/components/schemas/RateLimiterError' - 400: - description: 'Invalid user input' - content: - application/json: - schema: - $ref: '#/components/schemas/InvalidInputError' - - 401: - description: 'Authentication error' - content: - application/json: - schema: - $ref: '#/components/schemas/AuthenticationError' - 403: - description: 'Authorization error' - content: - application/json: - schema: - $ref: '#/components/schemas/AuthorizationError' 422: description: 'Validation error' content: application/json: schema: $ref: '#/components/schemas/ValidationError' + 401: + description: 'Authentication error' + content: + application/json: + schema: + $ref: '#/components/schemas/AuthenticationError' + 400: + description: 'Invalid user input' + content: + application/json: + schema: + $ref: '#/components/schemas/InvalidInputError' parameters: X-API-SECRET: name: X-API-SECRET @@ -15199,873 +15205,193 @@ components: type: string example: JSON type: object - Meta: - properties: - pagination: - $ref: '#/components/schemas/Pagination' - Pagination: - type: object - properties: - total: - type: integer - description: 'The total number of items' - example: 1 - readOnly: true - count: - type: integer - description: 'The number of items per page' - example: 1 - readOnly: true - per_page: - type: integer - description: 'The number of items per page' - example: 1 - readOnly: true - current_page: - type: integer - description: 'The current page number' - example: 1 - readOnly: true - total_pages: - type: integer - description: 'The total number of pages' - example: 1 - readOnly: true - links: - type: object - description: 'The pagination links' - readOnly: true - Document: + Credit: properties: id: - description: 'The document hashed id' + description: "The unique hashed ID of the credit" type: string - example: AS3df3A + example: Opnel5aKBz user_id: - description: 'The user hashed id' + description: "The unique hashed ID of the user associated with the credit" type: string - example: '' + example: 1a2b3c4d5e assigned_user_id: - description: 'The assigned user hashed id' + description: "The unique hashed ID of the assigned user responsible for the credit" type: string - example: '' - project_id: - description: 'The project associated with this document' - type: string - example: '' - vendor_id: - description: 'The vendor associated with this documents' - type: string - example: '' - name: - description: 'The document name' - type: string - example: Beauty - url: - description: 'The document url' - type: string - example: Beauty - preview: - description: 'The document preview url' - type: string - example: Beauty - type: - description: 'The document type' - type: string - example: Beauty - disk: - description: 'The document disk' - type: string - example: Beauty - hash: - description: 'The document hashed' - type: string - example: Beauty - is_deleted: - description: 'Flag to determine if the document is deleted' - type: boolean - example: true - is_default: - description: 'Flag to determine if the document is a default doc' - type: boolean - example: true - created_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - updated_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - deleted_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - type: object - Error: - properties: - message: - description: 'Something terrible went wrong' - type: string - example: 'Unexpected error' - code: - description: 'The HTTP error code, ie 5xx 4xx' - type: integer - example: '500' - type: object - InvoiceItem: - type: object - properties: - quantity: - type: number - example: 1 - description: 'The quantity of the product offered for this line item' - cost: - type: number - format: float - example: 10.00 - description: 'The cost of the product offered for this line item' - product_key: - type: string - example: 'Product key' - description: 'The product key of the product offered for this line item (Referred to as Product in the product tab)' - product_cost: - type: number - format: float - example: 10.00 - description: 'The cost of the product offered for this line item (Referred to as Cost in the product tab)' - notes: - type: string - example: 'Item notes' - description: 'The notes/description for the product offered for this line item' - discount: - type: number - format: float - example: 5.00 - description: 'The discount applied to the product offered for this line item' - is_amount_discount: - type: boolean - example: false - description: 'Indicates whether the discount applied to the product offered for this line item is a fixed amount or a percentage' - tax_name1: - type: string - example: 'GST' - description: 'The name of the first tax applied to the product offered for this line item' - tax_rate1: - type: number - format: float - example: 10.00 - description: 'The rate of the first tax applied to the product offered for this line item' - tax_name2: - type: string - example: 'VAT' - description: 'The name of the second tax applied to the product offered for this line item' - tax_rate2: - type: number - format: float - example: 5.00 - description: 'The rate of the second tax applied to the product offered for this line item' - tax_name3: - type: string - example: 'CA Sales Tax' - description: 'The name of the third tax applied to the product offered for this line item' - tax_rate3: - type: number - format: float - example: 3.00 - description: 'The rate of the third tax applied to the product offered for this line item' - sort_id: - type: string - example: '0' - description: 'Deprecated' - deprecated: true - line_total: - type: number - format: float - example: 10.00 - description: 'The total amount of the product offered for this line item' - readOnly: true - gross_line_total: - type: number - format: float - example: 15.00 - description: 'The total amount of the product offered for this line item before discounts' - readOnly: true - tax_amount: - type: number - format: float - example: 1.00 - description: 'The total amount of tax applied to the product offered for this line item' - readOnly: true - date: - type: string - format: date-time - example: '2023-03-19T00:00:00Z' - description: 'Deprecated' - deprecated: true - custom_value1: - type: string - example: 'Custom value 1' - description: 'The first custom value of the product offered for this line item' - custom_value2: - type: string - example: 'Custom value 2' - description: 'The second custom value of the product offered for this line item' - custom_value3: - type: string - example: 'Custom value 3' - description: 'The third custom value of the product offered for this line item' - custom_value4: - type: string - example: 'Custom value 4' - description: 'The fourth custom value of the product offered for this line item' - type_id: - type: string - example: '1' - description: '1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense' - default: '1' - tax_id: - type: string - example: '1' - default: '1' - description: 'The tax ID of the product: 1 product, 2 service, 3 digital, 4 shipping, 5 exempt, 5 reduced tax, 7 override, 8 zero rate, 9 reverse tax' - ExpenseCategory: - properties: - id: - description: 'The expense hashed id' - type: string - example: Opnel5aKBz - name: - description: 'The expense category name' - type: string - example: Accounting - user_id: - description: 'The user hashed id' - type: string - example: XS987sD - is_deleted: - description: 'Flag determining whether the expense category has been deleted' - type: boolean - example: true - updated_at: - description: 'The updated at timestamp' - type: integer - example: '2' - created_at: - description: 'The created at timestamp' - type: integer - example: '2' - type: object - Client: - properties: - id: - description: 'The unique identifier of the client' - type: string - example: Opnel5aKBz - readOnly: true - contacts: - type: array - items: - $ref: '#/components/schemas/ClientContact' - user_id: - description: 'The unique identifier of the user who created the client' - type: string - example: Ua6Rw4pVbS - readOnly: true - assigned_user_id: - description: 'The unique identifier of the user who has been assigned the client' - type: string - example: Ua6Rw4pVbS - name: - description: 'The name of the client company or organization' - type: string - example: "Jim's Housekeeping" - website: - description: 'The website URL of the client company or organization' - type: string - example: 'https://www.jims-housekeeping.com' - private_notes: - description: 'Notes that are only visible to the user who created the client' - type: string - example: 'Client prefers email communication over phone calls' - client_hash: - description: 'A unique hash value for the client' - type: string - example: asdfkjhk342hjhbfdvmnfb1 - readOnly: true - industry_id: - description: 'The unique identifier of the industry the client operates in' - type: number - example: '5' - size_id: - description: 'The unique identifier for the size category of the client company or organization' - type: number - example: '2' - address1: - description: "First line of the client's address" - type: string - example: '123 Main St' - address2: - description: "Second line of the client's address, if needed" - type: string - example: 'Apt 4B' - city: - description: 'The city the client is located in' - type: string - example: 'Beverly Hills' - state: - description: 'The state, province, or locality the client is located in' - type: string - example: 'California' - postal_code: - description: 'The postal code or ZIP code of the client' - type: string - example: '90210' - phone: - description: "The client's phone number" - type: string - example: '555-3434-3434' - country_id: - description: "The unique identifier of the client's country" - type: number - format: integer - example: '1' - custom_value1: - description: 'A custom field for storing additional information' - type: string - example: 'Preferred contact: Email' - custom_value2: - description: 'A custom field for storing additional information' - type: string - example: 'Account manager: John Doe' - custom_value3: - description: 'A custom field for storing additional information' - type: string - example: 'VIP client: Yes' - custom_value4: - description: 'A custom field for storing additional information' - type: string - example: 'Annual contract value: $50,000' - vat_number: - description: "The client's VAT (Value Added Tax) number, if applicable" - type: string - example: 'VAT123456' - id_number: - description: 'A unique identification number for the client, such as a tax ID or business registration number' - type: string - number: - description: 'A system-assigned unique number for the client, typically used for invoicing purposes' - type: string - example: 'CL-0001' - shipping_address1: - description: "First line of the client's shipping address" - type: string - example: '5 Wallaby Way' - shipping_address2: - description: "Second line of the client's shipping address, if needed" - type: string - example: 'Suite 5' - shipping_city: - description: "The city of the client's shipping address" - type: string - example: 'Perth' - shipping_state: - description: "The state, province, or locality of the client's shipping address" - type: string - example: 'Western Australia' - shipping_postal_code: - description: "The postal code or ZIP code of the client's shipping address" - type: string - example: '6110' - shipping_country_id: - description: "The unique identifier of the country for the client's shipping address" - type: number - format: integer - example: '4' - is_deleted: - description: 'A boolean value indicating whether the client has been deleted or not' - type: boolean - example: false - readOnly: true - balance: - description: 'The outstanding balance the client owes' - type: number - format: float - example: '500.00' - readOnly: true - paid_to_date: - description: 'The total amount the client has paid to date' - type: number - format: float - example: '2000.00' - readOnly: true - credit_balance: - description: 'The available credit balance for the client to use on future purchases' - type: number - format: float - example: '100.00' - readOnly: true - last_login: - description: "The timestamp of the client's last login" - type: number - format: integer - example: '1628686031' - readOnly: true - created_at: - description: 'The timestamp when the client was created' - type: number - format: integer - example: '1617629031' - readOnly: true - updated_at: - description: 'The timestamp when the client was last updated' - type: number - format: integer - example: '1628445631' - readOnly: true - group_settings_id: - description: 'The group settings assigned to the client' - type: string - example: Opnel5aKBz - routing_id: - description: 'The routing address id for e-invoicing for this client' - type: string - example: Opnel5aKBz3489-dfkiu-2239-sdsd - is_tax_exempt: - description: 'Flag which defines if the client is exempt from taxes' - type: boolean - example: false - has_valid_vat_number: - description: 'Flag which defines if the client has a valid VAT number' - type: boolean - example: false - readOnly: true - payment_balance: - description: 'Defines the payment balance the client has on file (pre payments / over payments / unapplied amounts)' - type: number - example: 100 - readOnly: true - settings: - $ref: '#/components/schemas/ClientSettings' - type: object - Quote: - properties: - id: - description: 'The unique hashed identifier for the quote' - type: string - example: Opnel5aKBz - user_id: - description: 'The unique hashed identifier for the user who created the quote' - type: string - example: '' - assigned_user_id: - description: 'The unique hashed identifier for the user assigned to the quote' - type: string - example: '' + example: 6f7g8h9i0j client_id: - description: 'The unique hashed identifier for the client associated with the quote' + description: "The unique hashed ID of the client associated with the credit" type: string - example: '' + example: p1q2r3s4t5 status_id: - description: 'The status of the quote represented by a unique identifier' + description: "The ID representing the current status of the credit" type: string - example: '' + example: 3 + invoice_id: + description: "The unique hashed ID of the linked invoice to which the credit is applied" + type: string + example: u1v2w3x4y5 number: - description: 'The unique alpha-numeric quote number for the quote per company' + description: "The unique alphanumeric credit number per company" type: string example: QUOTE_101 po_number: - description: 'The purchase order number associated with the quote' + description: "The purchase order number referred to by the credit" type: string - example: PO-1234 + example: PO_12345 terms: - description: 'The terms and conditions for the quote' + description: "The terms associated with the credit" type: string - example: 'These are some quote terms. Valid for 14 days.' + example: "Net 30" public_notes: - description: 'Publicly visible notes associated with the quote' + description: "Public notes for the credit" type: string - example: 'These are public notes which the client may see' + example: "Thank you for your business." private_notes: - description: 'Privately visible notes associated with the quote, not disclosed to the client' + description: "Private notes for internal use, not visible to the client" type: string - example: 'These are private notes, not to be disclosed to the client' + example: "Client is requesting a discount." footer: - description: 'The footer text of the quote' + description: "The footer text for the credit" type: string - example: 'The text goes in the footer of the quote' + example: "Footer text goes here." custom_value1: - description: 'First custom value field for additional information' + description: "Custom value 1 for additional credit information" type: string - example: 'A custom value' + example: "Custom data 1" custom_value2: - description: 'Second custom value field for additional information' + description: "Custom value 2 for additional credit information" type: string - example: 'A custom value' + example: "Custom data 2" custom_value3: - description: 'Third custom value field for additional information' + description: "Custom value 3 for additional credit information" type: string - example: 'A custom value' + example: "Custom data 3" custom_value4: - description: 'Fourth custom value field for additional information' + description: "Custom value 4 for additional credit information" type: string - example: 'A custom value' + example: "Custom data 4" tax_name1: - description: 'The name of the first tax applied to the quote' + description: "The name of the first tax applied to the credit" type: string - example: GST + example: "VAT" tax_name2: - description: 'The name of the second tax applied to the quote' + description: "The name of the second tax applied to the credit" type: string - example: VAT + example: "GST" tax_rate1: - description: 'The rate of the first tax applied to the quote' + description: "The rate of the first tax applied to the credit" type: number format: float example: 10.00 tax_rate2: - description: 'The rate of the second tax applied to the quote' + description: "The rate of the second tax applied to the credit" type: number format: float - example: 10.00 + example: 5.00 tax_name3: - description: 'The name of the third tax applied to the quote' + description: "The name of the third tax applied to the credit" type: string - example: '' + example: "PST" tax_rate3: - description: 'The rate of the third tax applied to the quote' + description: "The rate of the third tax applied to the credit" type: number format: float - example: 10.00 + example: 8.00 total_taxes: - description: 'The total amount of taxes for the quote' + description: "The total amount of taxes for the credit" type: number format: float - example: 10.00 + example: 23.00 line_items: type: array - description: 'An array of objects which define the line items of the quote' + description: 'An array of objects which define the line items of the credit' items: $ref: '#/components/schemas/InvoiceItem' amount: - description: 'The total amount of the quote before taxes and discounts' + description: "The total amount of the credit" type: number format: float - example: 10.00 + example: 100.00 balance: - description: 'The balance due for the quote after accounting for payments' + description: "The outstanding balance of the credit" type: number format: float - example: 10.00 + example: 50.00 paid_to_date: - description: 'The total amount paid on the quote so far' + description: "The total amount paid to date for the credit" type: number format: float - example: 10.00 + example: 50.00 discount: - description: 'The discount amount or percentage applied to the quote' + description: "The discount applied to the credit" type: number format: float example: 10.00 partial: - description: 'The partial or deposit amount for the quote' + description: "The partial amount applied to the credit" type: number format: float - example: 10.00 + example: 20.00 is_amount_discount: - description: 'Boolean flag indicating if the discount is a fixed amount or a percentage' + description: "Indicates whether the discount applied is a fixed amount or a percentage" type: boolean example: true is_deleted: - description: 'Boolean flag indicating if the quote has been deleted' + description: "Indicates whether the credit has been deleted" type: boolean example: false uses_inclusive_taxes: - description: 'Boolean flag indicating if the taxes used are inclusive or exclusive' + description: "Indicates whether the tax rates applied to the credit are inclusive or exclusive" type: boolean example: true date: - description: 'The date the quote was created' + description: "The date the credit was issued" type: string format: date - example: '1994-07-30' + example: "1994-07-30" last_sent_date: - description: 'The last date the quote was sent to the client' + description: "The date the credit was last sent out" type: string format: date - example: '1994-07-30' + example: "1994-07-30" next_send_date: - description: 'The next scheduled date for sending a reminder for the quote' + description: "The next scheduled date for sending a credit reminder" type: string format: date - example: '1994-07-30' + example: "1994-07-30" partial_due_date: - description: 'The due date for the partial or deposit amount' + description: "The due date for the partial amount of the credit" type: string format: date - example: '1994-07-30' + example: "1994-07-30" due_date: - description: 'The due date for the total amount of the quote' + description: "The due date for the total amount of the credit" type: string format: date - example: '1994-07-30' + example: "1994-07-30" settings: - $ref: '#/components/schemas/CompanySettings' + $ref: "#/components/schemas/CompanySettings" last_viewed: - description: 'The timestamp of the last time the quote was viewed' + description: "The timestamp of the last time the credit was viewed" type: number format: integer example: 1434342123 updated_at: - description: 'The timestamp of the last update to the quote' + description: "The timestamp of the last time the credit was updated" type: number format: integer example: 1434342123 archived_at: - description: 'The timestamp of when the quote was archived' + description: "The timestamp of the last time the credit was archived" type: number format: integer example: 1434342123 custom_surcharge1: - description: 'First custom surcharge amount for the quote' + description: "First custom surcharge amount" type: number format: float example: 10.00 - custom_surcharge2: - description: 'Second custom surcharge amount for the quote' - type: number - format: float - example: 10.00 - custom_surcharge3: - description: 'Third custom surcharge amount for the quote' - type: number - format: float - example: 10.00 - custom_surcharge4: - description: 'Fourth custom surcharge amount for the quote' - type: number - format: float - example: 10.00 - custom_surcharge_tax1: - description: 'Boolean flag indicating if taxes are charged on the first custom surcharge amount' - type: boolean - example: true - custom_surcharge_tax2: - description: 'Boolean flag indicating if taxes are charged on the second custom surcharge amount' - type: boolean - example: true - custom_surcharge_tax3: - description: 'Boolean flag indicating if taxes are charged on the third custom surcharge amount' - type: boolean - example: true - custom_surcharge_tax4: - description: 'Boolean flag indicating if taxes are charged on the fourth custom surcharge amount' - type: boolean - example: true - type: object - InvoiceRequest: - required: - - client_id - properties: - id: - description: 'The invoice hashed id' - type: string - example: Opnel5aKBz - readOnly: true - user_id: - description: 'The user hashed id' - type: string - example: Opnel5aKBz - assigned_user_id: - description: 'The assigned user hashed id' - type: string - example: Opnel5aKBz - client_id: - description: 'The client hashed id' - type: string - example: Opnel5aKBz - status_id: - description: 'The invoice status variable' - type: string - example: '4' - readOnly: true - number: - description: 'The invoice number - is a unique alpha numeric number per invoice per company' - type: string - example: INV_101 - po_number: - description: 'The purchase order associated with this invoice' - type: string - example: PO-1234 - terms: - description: 'The invoice terms' - type: string - example: 'These are invoice terms' - public_notes: - description: 'The public notes of the invoice' - type: string - example: 'These are some public notes' - private_notes: - description: 'The private notes of the invoice' - type: string - example: 'These are some private notes' - footer: - description: 'The invoice footer notes' - type: string - example: '' - custom_value1: - description: 'A custom field value' - type: string - example: '2022-10-01' - custom_value2: - description: 'A custom field value' - type: string - example: 'Something custom' - custom_value3: - description: 'A custom field value' - type: string - example: '' - custom_value4: - description: 'A custom field value' - type: string - example: '' - tax_name1: - description: 'The tax name' - type: string - example: '' - tax_name2: - description: 'The tax name' - type: string - example: '' - tax_rate1: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_rate2: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_name3: - description: 'The tax name' - type: string - example: '' - tax_rate3: - description: 'The tax rate' - type: number - format: float - example: '10.00' - total_taxes: - description: 'The total taxes for the invoice' - type: number - format: float - example: '10.00' - readOnly: true - line_items: - type: array - description: 'An array of objects which define the line items of the invoice' - items: - $ref: '#/components/schemas/InvoiceItem' - invitations: - type: array - description: 'An array of objects which define the invitations of the invoice' - items: - $ref: '#/components/schemas/InvoiceInvitationRequest' - amount: - description: 'The invoice amount' - type: number - format: float - example: '10.00' - readOnly: true - balance: - description: 'The invoice balance' - type: number - format: float - example: '10.00' - readOnly: true - paid_to_date: - description: 'The amount paid on the invoice to date' - type: number - format: float - example: '10.00' - readOnly: true - discount: - description: 'The invoice discount, can be an amount or a percentage' - type: number - format: float - example: '10.00' - partial: - description: 'The deposit/partial amount' - type: number - format: float - example: '10.00' - is_amount_discount: - description: 'Flag determining if the discount is an amount or a percentage' - type: boolean - example: true - is_deleted: - description: 'Defines if the invoice has been deleted' - type: boolean - example: true - readOnly: true - uses_inclusive_taxes: - description: 'Defines the type of taxes used as either inclusive or exclusive' - type: boolean - example: true - date: - description: 'The Invoice Date' - type: string - format: date - example: '1994-07-30' - last_sent_date: - description: 'The last date the invoice was sent out' - type: string - format: date - example: '1994-07-30' - readOnly: true - next_send_date: - description: 'The Next date for a reminder to be sent' - type: string - format: date - example: '1994-07-30' - readOnly: true - partial_due_date: - description: 'The due date for the deposit/partial amount' - type: string - format: date - example: '1994-07-30' - due_date: - description: 'The due date of the invoice' - type: string - format: date - example: '1994-07-30' - last_viewed: - description: Timestamp - type: number - format: integer - example: '1434342123' - readOnly: true - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - readOnly: true - archived_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - readOnly: true - custom_surcharge1: - description: 'First Custom Surcharge' - type: number - format: float - example: '10.00' custom_surcharge2: description: 'Second Custom Surcharge' type: number @@ -16097,10 +15423,21 @@ components: description: 'Toggles charging taxes on custom surcharge amounts' type: boolean example: true - project_id: - description: 'The project associated with this invoice' + type: object + + GenericBulkAction: + properties: + action: type: string - example: Opnel5aKBz + example: archive + description: 'The action to perform ie. archive / restore / delete' + ids: + type: array + items: + format: string + type: string + example: 2J234DFA,D2J234DFA,D2J234DFA + description: string array of client hashed ids type: object ProductRequest: type: object @@ -16262,517 +15599,274 @@ components: ``` example: '1' - InvoiceInvitationRequest: - required: - - client_contact_id + CompanyLedger: properties: - id: - description: 'The entity invitation hashed id' - type: string - example: Opnel5aKBz - readOnly: true - client_contact_id: - description: 'The client contact hashed id' - type: string - example: Opnel5aKBz - key: - description: 'The invitation key' - type: string - example: Opnel5aKBz4343343566236gvbb - readOnly: true - link: - description: 'The invitation link' - type: string - example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' - readOnly: true - sent_date: - description: 'The invitation sent date' - type: string - format: date-time - readOnly: true - viewed_date: - description: 'The invitation viewed date' - type: string - format: date-time - readOnly: true - opened_date: - description: 'The invitation opened date' - type: string - format: date-time - readOnly: true - updated_at: - description: 'Timestamp' - type: number - format: integer - example: '1434342123' - readOnly: true - archived_at: - description: 'Timestamp' - type: number - format: integer - example: '1434342123' - readOnly: true - email_error: - description: 'The email error' - type: string - example: 'The email error' - readOnly: true - email_status: - description: 'The email status' - type: string - readOnly: true - - BankIntegration: - properties: - id: - description: 'The bank integration hashed id' + entity_id: + description: 'This field will reference one of the following entity hashed ID payment_id, invoice_id or credit_id' type: string example: AS3df3A - user_id: - description: 'The user hashed id' + notes: + description: 'The notes which reference this entry of the ledger' type: string - example: AS3df3A - provider_bank_name: - description: 'The providers bank name' - type: string - example: 'Chase Bank' - bank_account_id: - description: 'The bank account id' - type: integer - example: '1233434' - bank_account_name: - description: 'The name of the account' - type: string - example: 'My Checking Acc' - bank_account_number: - description: 'The account number' - type: string - example: '111 234 2332' - bank_account_status: - description: 'The status of the bank account' - type: string - example: ACTIVE - bank_account_type: - description: 'The type of account' - type: string - example: CREDITCARD + example: 'Credit note for invoice #3212' balance: - description: 'The current bank balance if available' - type: number - example: '1000000' - currency: - description: 'iso_3166_3 code' - type: string - example: USD - type: object - CompanyToken: - properties: - name: - description: 'The token name' - type: string - example: 'Token Name' - token: - description: 'The token value' - type: string - example: AS3df3jUUH765fhfd9KJuidj3JShjA - is_system: - description: 'Determines whether the token is created by the system rather than a user' - type: boolean - example: 'true' - type: object - PurchaseOrder: - properties: - id: - description: 'The unique hashed identifier for the purchase order' - type: string - example: Opnel5aKBz - user_id: - description: 'The unique hashed identifier for the user who created the purchase order' - type: string - example: '' - assigned_user_id: - description: 'The unique hashed identifier for the user assigned to the purchase order' - type: string - example: '' - vendor_id: - description: 'The unique hashed identifier for the vendor associated with the purchase order' - type: string - example: '' - status_id: - description: 'The status of the purchase order represented by a unique identifier' - type: string - example: '' - number: - description: 'The unique alpha-numeric purchase order number per company' - type: string - example: PO_101 - quote_number: - description: 'The quote number associated with this purchase order' - type: string - example: QUOTE_101 - terms: - description: 'The terms and conditions for the purchase order' - type: string - example: 'These are some purchase order terms. Valid for 14 days.' - public_notes: - description: 'Publicly visible notes associated with the purchase order' - type: string - example: 'These are public notes which the vendor may see' - private_notes: - description: 'Privately visible notes associated with the purchase order, not disclosed to the vendor' - type: string - example: 'These are private notes, not to be disclosed to the vendor' - footer: - description: 'The footer text of the purchase order' - type: string - example: 'The text goes in the footer of the purchase order' - custom_value1: - description: 'First custom value field for additional information' - type: string - example: 'A custom value' - custom_value2: - description: 'Second custom value field for additional information' - type: string - example: 'A custom value' - custom_value3: - description: 'Third custom value field for additional information' - type: string - example: 'A custom value' - custom_value4: - description: 'Fourth custom value field for additional information' - type: string - example: 'A custom value' - tax_name1: - description: 'The name of the first tax applied to the purchase order' - type: string - example: GST - tax_name2: - description: 'The name of the second tax applied to the purchase order' - type: string - example: VAT - tax_rate1: - description: 'The rate of the first tax applied to the purchase order' + description: 'The client balance' type: number format: float - example: 10.00 - tax_rate2: - description: 'The rate of the second tax applied to the purchase order' + example: '10.00' + adjustment: + description: 'The amount the client balance is adjusted by' type: number format: float - example: 10.00 - tax_name3: - description: 'The name of the third tax applied to the purchase order' - type: string - example: '' - tax_rate3: - description: 'The rate of the third tax applied to the purchase order' - type: number - format: float - example: 10.00 - total_taxes: - description: 'The total amount of taxes applied to the purchase order' - type: number - format: float - example: 10.00 - line_items: - type: array - description: 'An array of objects which define the line items of the purchase order' - items: - $ref: '#/components/schemas/InvoiceItem' - amount: - description: 'The total amount of the purchase order before taxes and discounts' - type: number - format: float - example: 10.00 - balance: - description: 'The balance due for the purchase order after accounting for payments' - type: number - format: float - example: 10.00 - paid_to_date: - description: 'The total amount paid on the purchase order so far' - type: number - format: float - example: 10.00 - discount: - description: 'The discount amount or percentage applied to the purchase order' - type: number - format: float - example: 10.00 - partial: - description: 'The partial or deposit amount for the purchase order' - type: number - format: float - example: 10.00 - is_amount_discount: - description: 'Boolean flag indicating if the discount is a fixed amount or a percentage' - type: boolean - example: true - is_deleted: - description: 'Boolean flag indicating if the purchase order has been deleted' - type: boolean - example: false - uses_inclusive_taxes: - description: 'Boolean flag indicating if the taxes used are inclusive or exclusive' - type: boolean - example: true - date: - description: 'The date the purchase order was created' - type: string - format: date - example: '1994-07-30' - last_sent_date: - description: 'The last date the purchase order was sent to the vendor' - type: string - format: date - example: '1994-07-30' - next_send_date: - description: 'The next scheduled date for sending a reminder for the purchase order' - type: string - format: date - example: '1994-07-30' - partial_due_date: - description: 'The due date for the partial or deposit amount' - type: string - format: date - example: '1994-07-30' - due_date: - description: 'The due date for the total amount of the purchase order' - type: string - format: date - example: '1994-07-30' - settings: - $ref: '#/components/schemas/CompanySettings' - last_viewed: - description: Timestamp - type: number - format: integer - example: 1434342123 + example: '10.00' updated_at: description: Timestamp type: number format: integer - example: 1434342123 - archived_at: - description: Timestamp - type: number - format: integer - example: 1434342123 - custom_surcharge1: - description: 'First custom surcharge amount for the purchase order' - type: number - format: float - example: 10.00 - custom_surcharge2: - description: 'Second custom surcharge amount for the purchase order' - type: number - format: float - example: 10.00 - custom_surcharge3: - description: 'Third custom surcharge amount for the purchase order' - type: number - format: float - example: 10.00 - custom_surcharge4: - description: 'Fourth custom surcharge amount for the purchase order' - type: number - format: float - example: 10.00 - custom_surcharge_tax1: - description: 'Boolean flag indicating if taxes are charged on the first custom surcharge amount' - type: boolean - example: true - custom_surcharge_tax2: - description: 'Boolean flag indicating if taxes are charged on the second custom surcharge amount' - type: boolean - example: true - custom_surcharge_tax3: - description: 'Boolean flag indicating if taxes are charged on the third custom surcharge amount' - type: boolean - example: true - custom_surcharge_tax4: - description: 'Boolean flag indicating if taxes are charged on the fourth custom surcharge amount' - type: boolean - example: true - type: object - - - InvoiceInvitation: - properties: - id: - description: 'The entity invitation hashed id' - type: string - example: Opnel5aKBz - readOnly: true - client_contact_id: - description: 'The client contact hashed id' - type: string - example: Opnel5aKBz - key: - description: 'The invitation key' - type: string - example: Opnel5aKBz4343343566236gvbb - readOnly: true - link: - description: 'The invitation link' - type: string - example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' - readOnly: true - sent_date: - description: 'The invitation sent date' - type: string - format: date-time - readOnly: true - viewed_date: - description: 'The invitation viewed date' - type: string - format: date-time - readOnly: true - opened_date: - description: 'The invitation opened date' - type: string - format: date-time - readOnly: true - updated_at: - description: 'Timestamp' - type: number - format: integer example: '1434342123' - readOnly: true - archived_at: - description: 'Timestamp' - type: number - format: integer - example: '1434342123' - readOnly: true - email_error: - description: 'The email error' - type: string - example: 'The email error' - readOnly: true - email_status: - description: 'The email status' - type: string - readOnly: true - - VendorContact: - properties: - id: - description: 'The hashed id of the vendor contact' - type: string - example: Opnel5aKBz - readOnly: true - user_id: - description: 'The hashed id of the user id' - type: string - example: Opnel5aKBz - readOnly: true - vendor_id: - description: 'The hashed id of the vendor' - type: string - example: Opnel5aKBz - readOnly: true - first_name: - description: 'The first name of the contact' - type: string - example: Harry - last_name: - description: 'The last name of the contact' - type: string - example: Windsor - contact_key: - description: 'A unique identifier for the contact' - type: string - example: JD0X52bkfZlJRiroCJ0tcSiAjsJTntZ5uqKdiZ0a - readOnly: true - confirmation_code: - description: 'The confirmation code used to authenticate the contacts email address' - type: string - example: 333-sdjkh34gbasd - readOnly: true - phone: - description: 'The contacts phone number' - type: string - example: 555-123-1234 - custom_value1: - description: 'A custom value' - type: string - example: '2022-10-10' - custom_value2: - description: 'A custom value' - type: string - example: $1000 - custom_value3: - description: 'A custom value' - type: string - example: '' - custom_value4: - description: 'A custom value' - type: string - example: '' - email: - description: 'The contact email address' - type: string - example: harry@windsor.com - email_verified_at: - description: 'The date which the contact confirmed their email' - type: number - format: integer - example: '134341234234' - readOnly: true - password: - description: 'The hashed password of the contact' - type: string - example: '*****' - is_primary: - description: 'Boolean flag determining if the contact is the primary contact for the vendor' - type: boolean - example: true created_at: description: Timestamp type: number format: integer - example: '134341234234' + example: '1434342123' + type: object + Invoice: + properties: + id: + description: 'The invoice hashed id' + type: string + example: Opnel5aKBz readOnly: true + user_id: + description: 'The user hashed id' + type: string + example: Opnel5aKBz + readOnly: true + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: Opnel5aKBz + client_id: + description: 'The client hashed id' + type: string + example: Opnel5aKBz + status_id: + description: 'The invoice status variable' + type: string + example: '4' + number: + description: 'The invoice number - is a unique alpha numeric number per invoice per company' + type: string + example: INV_101 + po_number: + description: 'The purchase order associated with this invoice' + type: string + example: PO-1234 + terms: + description: 'The invoice terms' + type: string + example: 'These are invoice terms' + public_notes: + description: 'The public notes of the invoice' + type: string + example: 'These are some public notes' + private_notes: + description: 'The private notes of the invoice' + type: string + example: 'These are some private notes' + footer: + description: 'The invoice footer notes' + type: string + example: '' + custom_value1: + description: 'A custom field value' + type: string + example: '2022-10-01' + custom_value2: + description: 'A custom field value' + type: string + example: 'Something custom' + custom_value3: + description: 'A custom field value' + type: string + example: '' + custom_value4: + description: 'A custom field value' + type: string + example: '' + tax_name1: + description: 'The tax name' + type: string + example: '' + tax_name2: + description: 'The tax name' + type: string + example: '' + tax_rate1: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_rate2: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_name3: + description: 'The tax name' + type: string + example: '' + tax_rate3: + description: 'The tax rate' + type: number + format: float + example: '10.00' + total_taxes: + description: 'The total taxes for the invoice' + type: number + format: float + example: '10.00' + line_items: + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' + invitations: + type: array + description: 'An array of objects which define the invitations of the invoice' + items: + $ref: '#/components/schemas/InvoiceInvitation' + amount: + description: 'The invoice amount' + type: number + format: float + example: '10.00' + balance: + description: 'The invoice balance' + type: number + format: float + example: '10.00' + paid_to_date: + description: 'The amount paid on the invoice to date' + type: number + format: float + example: '10.00' + discount: + description: 'The invoice discount, can be an amount or a percentage' + type: number + format: float + example: '10.00' + partial: + description: 'The deposit/partial amount' + type: number + format: float + example: '10.00' + is_amount_discount: + description: 'Flag determining if the discount is an amount or a percentage' + type: boolean + example: true + is_deleted: + description: 'Defines if the invoice has been deleted' + type: boolean + example: true + uses_inclusive_taxes: + description: 'Defines the type of taxes used as either inclusive or exclusive' + type: boolean + example: true + date: + description: 'The Invoice Date' + type: string + format: date + example: '1994-07-30' + last_sent_date: + description: 'The last date the invoice was sent out' + type: string + format: date + example: '1994-07-30' + next_send_date: + description: 'The Next date for a reminder to be sent' + type: string + format: date + example: '1994-07-30' + partial_due_date: + description: 'The due date for the deposit/partial amount' + type: string + format: date + example: '1994-07-30' + due_date: + description: 'The due date of the invoice' + type: string + format: date + example: '1994-07-30' + last_viewed: + description: Timestamp + type: number + format: integer + example: '1434342123' updated_at: description: Timestamp type: number format: integer - example: '134341234234' - readOnly: true - deleted_at: + example: '1434342123' + archived_at: description: Timestamp type: number format: integer - example: '134341234234' - readOnly: true - type: object - ClientGatewayToken: - properties: - id: - description: 'The hashed id of the client gateway token' + example: '1434342123' + custom_surcharge1: + description: 'First Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge2: + description: 'Second Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge3: + description: 'Third Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge4: + description: 'Fourth Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge_tax1: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax2: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax3: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax4: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + project_id: + description: 'The project associated with this invoice' type: string example: Opnel5aKBz - client_id: - description: 'The hashed_id of the client' - type: string - example: '2' - token: - description: 'The payment token' - type: string - example: '2' - routing_number: - description: 'THe bank account routing number' - type: string - example: '2' - company_gateway_id: - description: 'The hashed id of the company gateway' - type: string - example: '2' - is_default: - description: 'Flag determining if the token is the default payment method' + auto_bill_tries: + description: 'The number of times the invoice has attempted to be auto billed' + type: integer + example: '1' + readOnly: true + auto_bill_enabled: + description: 'Boolean flag determining if the invoice is set to auto bill' type: boolean - example: 'true' + example: true + subscription_id: + description: 'The subscription associated with this invoice' + type: string + example: Opnel5aKBz + type: object Company: properties: @@ -16833,7 +15927,44 @@ components: type: boolean example: true custom_fields: - description: "A mapping of custom fields for various objects within the company" + description: | + A mapping of custom fields for various objects within the company + + **note** + The custom fields object is formatted as follows + + key : value + entity(integer) : label|type + + for example: + "company1": "Number|single_line_text", + + This defines the first company custom field, with label Number which has a custom field type of a single text line + + Supported entity types + + invoice + product + client + contact + task + user + project + vendor + expense + payment + + Supported input types values + + single_line_text + date + switch + + For text areas, you only need to supply the label ie "TextArea", the | and type values are not required. + For the dropdown the data format is as follows: + + label|your,drop,down,values,in,comma,separated,format + type: object enable_product_cost: description: "A flag determining whether to show or hide the product cost field in the user interface" @@ -17103,23 +16234,1035 @@ components: settings: $ref: '#/components/schemas/CompanySettings' type: object - CompanyLedger: + InvoiceRequest: + required: + - client_id properties: - entity_id: - description: 'This field will reference one of the following entity hashed ID payment_id, invoice_id or credit_id' + id: + description: 'The invoice hashed id' type: string - example: AS3df3A - notes: - description: 'The notes which reference this entry of the ledger' + example: Opnel5aKBz + readOnly: true + user_id: + description: 'The user hashed id' type: string - example: 'Credit note for invoice #3212' - balance: - description: 'The client balance' + example: Opnel5aKBz + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: Opnel5aKBz + client_id: + description: 'The client hashed id' + type: string + example: Opnel5aKBz + status_id: + description: 'The invoice status variable' + type: string + example: '4' + readOnly: true + number: + description: 'The invoice number - is a unique alpha numeric number per invoice per company' + type: string + example: INV_101 + po_number: + description: 'The purchase order associated with this invoice' + type: string + example: PO-1234 + terms: + description: 'The invoice terms' + type: string + example: 'These are invoice terms' + public_notes: + description: 'The public notes of the invoice' + type: string + example: 'These are some public notes' + private_notes: + description: 'The private notes of the invoice' + type: string + example: 'These are some private notes' + footer: + description: 'The invoice footer notes' + type: string + example: '' + custom_value1: + description: 'A custom field value' + type: string + example: '2022-10-01' + custom_value2: + description: 'A custom field value' + type: string + example: 'Something custom' + custom_value3: + description: 'A custom field value' + type: string + example: '' + custom_value4: + description: 'A custom field value' + type: string + example: '' + tax_name1: + description: 'The tax name' + type: string + example: '' + tax_name2: + description: 'The tax name' + type: string + example: '' + tax_rate1: + description: 'The tax rate' type: number format: float example: '10.00' - adjustment: - description: 'The amount the client balance is adjusted by' + tax_rate2: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_name3: + description: 'The tax name' + type: string + example: '' + tax_rate3: + description: 'The tax rate' + type: number + format: float + example: '10.00' + total_taxes: + description: 'The total taxes for the invoice' + type: number + format: float + example: '10.00' + readOnly: true + line_items: + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' + invitations: + type: array + description: 'An array of objects which define the invitations of the invoice' + items: + $ref: '#/components/schemas/InvoiceInvitationRequest' + amount: + description: 'The invoice amount' + type: number + format: float + example: '10.00' + readOnly: true + balance: + description: 'The invoice balance' + type: number + format: float + example: '10.00' + readOnly: true + paid_to_date: + description: 'The amount paid on the invoice to date' + type: number + format: float + example: '10.00' + readOnly: true + discount: + description: 'The invoice discount, can be an amount or a percentage' + type: number + format: float + example: '10.00' + partial: + description: 'The deposit/partial amount' + type: number + format: float + example: '10.00' + is_amount_discount: + description: 'Flag determining if the discount is an amount or a percentage' + type: boolean + example: true + is_deleted: + description: 'Defines if the invoice has been deleted' + type: boolean + example: true + readOnly: true + uses_inclusive_taxes: + description: 'Defines the type of taxes used as either inclusive or exclusive' + type: boolean + example: true + date: + description: 'The Invoice Date' + type: string + format: date + example: '1994-07-30' + last_sent_date: + description: 'The last date the invoice was sent out' + type: string + format: date + example: '1994-07-30' + readOnly: true + next_send_date: + description: 'The Next date for a reminder to be sent' + type: string + format: date + example: '1994-07-30' + readOnly: true + partial_due_date: + description: 'The due date for the deposit/partial amount' + type: string + format: date + example: '1994-07-30' + due_date: + description: 'The due date of the invoice' + type: string + format: date + example: '1994-07-30' + last_viewed: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + custom_surcharge1: + description: 'First Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge2: + description: 'Second Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge3: + description: 'Third Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge4: + description: 'Fourth Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge_tax1: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax2: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax3: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax4: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + project_id: + description: 'The project associated with this invoice' + type: string + example: Opnel5aKBz + type: object + SystemLog: + properties: + id: + description: 'The account hashed id' + type: string + example: AS3df3A + user_id: + description: 'The user_id hashed id' + type: string + example: AS3df3A + client_id: + description: 'The client_id hashed id' + type: string + example: AS3df3A + event_id: + description: 'The Log Type ID' + type: integer + example: 1 + category_id: + description: 'The Category Type ID' + type: integer + example: 1 + type_id: + description: 'The Type Type ID' + type: integer + example: 1 + log: + description: 'The json object of the error' + type: object + example: '{''key'':''value''}' + updated_at: + description: Timestamp + type: string + example: '2' + created_at: + description: Timestamp + type: string + example: '2' + type: object + Expense: + properties: + id: + description: 'The expense hashed id' + type: string + example: 'Opnel5aKBz' + user_id: + description: 'The user hashed id' + type: string + example: 'Opnel5aKBz' + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: 'Opnel5aKBz' + project_id: + description: 'The associated project_id' + type: string + example: 'Opnel5aKBz' + client_id: + description: 'The client hashed id' + type: string + example: 'Opnel5aKBz' + invoice_id: + description: 'The related invoice hashed id' + type: string + example: 'Opnel5aKBz' + bank_id: + description: 'The bank id related to this expense' + type: string + example: '' + invoice_currency_id: + description: 'The currency id of the related invoice' + type: string + example: '1' + currency_id: + description: 'The currency id of the expense' + type: string + example: '2' + invoice_category_id: + description: 'The invoice category id' + type: string + example: 'Opnel5aKBz' + payment_type_id: + description: 'The payment type id' + type: string + example: '' + recurring_expense_id: + description: 'The related recurring expense this expense was created from' + type: string + example: 'Opnel5aKBz' + private_notes: + description: 'The private notes of the expense' + type: string + example: '' + public_notes: + description: 'The public notes of the expense' + type: string + example: '' + transaction_reference: + description: 'The transaction references of the expense' + type: string + example: '' + transcation_id: + description: 'The transaction id of the expense' + type: string + example: '' + custom_value1: + description: 'A custom value' + type: string + example: '' + custom_value2: + description: 'A custom value' + type: string + example: '' + custom_value3: + description: 'A custom value' + type: string + example: '' + custom_value4: + description: 'A custom value' + type: string + example: '' + tax_amount: + description: 'The tax amount' + type: number + example: 10.00 + tax_name1: + description: 'Tax Name 1' + type: string + example: 'GST' + tax_name2: + description: 'Tax Name 2' + type: string + example: 'VAT' + tax_name3: + description: 'Tax Name 3' + type: string + example: 'IVA' + tax_rate1: + description: 'Tax rate 1' + type: number + format: float + example: '10.00' + tax_rate2: + description: 'Tax rate 2' + type: number + format: float + example: '10.00' + tax_rate3: + description: 'Tax rate 3' + type: number + format: float + example: '10.00' + amount: + description: 'The total expense amont' + type: number + format: float + example: '10.00' + foreign_amount: + description: 'The total foreign amount of the expense' + type: number + format: float + example: '10.00' + exchange_rate: + description: 'The exchange rate at the time of the expense' + type: number + format: float + example: '0.80' + date: + description: 'The expense date format Y-m-d' + type: string + example: '2022-12-01' + payment_date: + description: 'The date of payment for the expense, format Y-m-d' + type: string + example: '2022-12-01' + should_be_invoiced: + description: 'Flag whether the expense should be invoiced' + type: boolean + example: true + is_deleted: + description: 'Boolean determining whether the expense has been deleted' + type: boolean + example: true + invoice_documents: + description: 'Passing the expense documents over to the invoice' + type: boolean + example: true + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + type: object + BankTransaction: + properties: + id: + description: 'The bank integration hashed id' + type: string + example: AS3df3A + user_id: + description: 'The user hashed id' + type: string + example: AS3df3A + transaction_id: + description: 'The id of the transaction rule' + type: integer + example: 343434 + amount: + description: 'The transaction amount' + type: number + example: 10 + currency_id: + description: 'The currency ID of the currency' + type: string + example: '1' + account_type: + description: 'The account type' + type: string + example: creditCard + description: + description: 'The description of the transaction' + type: string + example: 'Potato purchases for kevin' + category_id: + description: 'The category id' + type: integer + example: 1 + category_type: + description: 'The category description' + type: string + example: Expenses + base_type: + description: 'Either CREDIT or DEBIT' + type: string + example: CREDIT + date: + description: 'The date of the transaction' + type: string + example: '2022-09-01' + bank_account_id: + description: 'The ID number of the bank account' + type: integer + example: '1' + type: object + ExpenseCategory: + properties: + id: + description: 'The expense hashed id' + type: string + example: Opnel5aKBz + name: + description: 'The expense category name' + type: string + example: Accounting + user_id: + description: 'The user hashed id' + type: string + example: XS987sD + is_deleted: + description: 'Flag determining whether the expense category has been deleted' + type: boolean + example: true + updated_at: + description: 'The updated at timestamp' + type: integer + example: '2' + created_at: + description: 'The created at timestamp' + type: integer + example: '2' + type: object + BankIntegration: + properties: + id: + description: 'The bank integration hashed id' + type: string + example: AS3df3A + user_id: + description: 'The user hashed id' + type: string + example: AS3df3A + provider_bank_name: + description: 'The providers bank name' + type: string + example: 'Chase Bank' + bank_account_id: + description: 'The bank account id' + type: integer + example: '1233434' + bank_account_name: + description: 'The name of the account' + type: string + example: 'My Checking Acc' + bank_account_number: + description: 'The account number' + type: string + example: '111 234 2332' + bank_account_status: + description: 'The status of the bank account' + type: string + example: ACTIVE + bank_account_type: + description: 'The type of account' + type: string + example: CREDITCARD + balance: + description: 'The current bank balance if available' + type: number + example: '1000000' + currency: + description: 'iso_3166_3 code' + type: string + example: USD + type: object + Subscription: + properties: + id: + description: Unique identifier for the subscription + type: string + example: Opnel5aKBz + user_id: + description: Unique identifier for the user associated with the subscription + type: string + example: Ua6Rw4pVbS + product_id: + description: Unique identifier for the product associated with the subscription + type: string + example: Pr5Ft7yBmC + recurring_invoice_id: + description: Unique identifier for the recurring invoice associated with the subscription + type: string + example: Ri2Yt8zJkP + is_recurring: + description: Indicates whether the subscription is recurring + type: boolean + example: 'true' + frequency_id: + description: 'integer const representation of the frequency' + type: string + example: '1' + auto_bill: + description: 'enum setting' + type: string + example: always + promo_code: + description: Promotional code applied to the subscription + type: string + example: PROMOCODE4U + promo_discount: + description: Discount percentage or amount applied to the subscription + type: number + example: 10 + is_amount_discount: + description: Indicates whether the discount is a fixed amount + type: boolean + example: 'true' + allow_cancellation: + description: Indicates whether the subscription can be cancelled + type: boolean + example: 'true' + per_seat_enabled: + description: Indicates whether the subscription pricing is per seat + type: boolean + example: 'true' + currency_id: + description: Unique identifier for the currency used in the subscription + type: integer + example: '1' + max_seats_limit: + description: Maximum number of seats allowed for the subscription + type: integer + example: '100' + trial_enabled: + description: Indicates whether the subscription has a trial period + type: boolean + example: 'true' + trial_duration: + description: Duration of the trial period in days + type: integer + example: '14' + allow_query_overrides: + description: Indicates whether query overrides are allowed for the subscription + type: boolean + example: 'true' + allow_plan_changes: + description: Indicates whether plan changes are allowed for the subscription + type: boolean + example: 'true' + refund_period: + description: Number of days within which refunds can be requested + type: integer + example: '30' + webhook_configuration: + description: Webhook configuration for the subscription + type: string + example: 'expand reference for this' + is_deleted: + description: Indicates whether the subscription has been deleted + type: boolean + example: 'false' + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + created_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + updated_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + type: object + BulkAction: + type: array + items: + type: integer + example: '[0,1,2,3,]' + FillableInvoice: + properties: + assigned_user_id: + description: "The assigned user's hashed ID" + type: string + example: 'a1b2c3d4' + client_id: + description: "The client's hashed ID" + type: string + example: 'x1y2z3a4' + number: + description: "The unique alphanumeric invoice number for each invoice per company" + type: string + example: INV_101 + po_number: + description: "The purchase order number associated with the invoice" + type: string + example: 'PO12345' + terms: + description: "The terms and conditions for the invoice" + type: string + example: 'Net 30' + public_notes: + description: "Public notes visible to the client on the invoice" + type: string + example: 'Thank you for your business.' + private_notes: + description: "Private notes for internal use only" + type: string + example: 'Client is a slow payer.' + footer: + description: "The footer text displayed on the invoice" + type: string + example: 'Authorized Signature' + custom_value1: + description: "First custom value for additional information" + type: string + example: 'Project ABC' + custom_value2: + description: "Second custom value for additional information" + type: string + example: 'Department XYZ' + custom_value3: + description: "Third custom value for additional information" + type: string + example: 'Location 123' + custom_value4: + description: "Fourth custom value for additional information" + type: string + example: 'Currency USD' + tax_name1: + description: "Name of the first tax applied to the invoice" + type: string + example: 'VAT' + tax_name2: + description: "Name of the second tax applied to the invoice" + type: string + example: 'GST' + tax_rate1: + description: "Rate of the first tax applied to the invoice" + type: number + example: 10.00 + tax_rate2: + description: "Rate of the second tax applied to the invoice" + type: number + example: 5.00 + tax_name3: + description: "Name of the third tax applied to the invoice" + type: string + example: 'PST' + tax_rate3: + description: "Rate of the third tax applied to the invoice" + type: number + example: 8.00 + line_items: + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' + discount: + description: "The discount applied to the invoice" + type: number + example: 10.00 + partial: + description: "The partial amount applied to the invoice" + type: number + example: 20.00 + is_amount_discount: + description: "Indicates whether the discount applied is a fixed amount or a percentage" + type: boolean + example: true + uses_inclusive_taxes: + description: "Indicates whether the tax rates applied to the invoice are inclusive or exclusive" + type: boolean + example: true + date: + description: "The date the invoice was issued" + type: string + example: '1994-07-30' + partial_due_date: + description: "The due date for the partial payment" + type: string + example: '1994-08-15' + due_date: + description: "The due date for the invoice" + type: string + example: '1994-08-30' + custom_surcharge1: + description: "First custom surcharge applied to the invoice" + type: number + example: 10.00 + custom_surcharge2: + description: "Second custom surcharge applied to the invoice" + type: number + example: 15.00 + custom_surcharge3: + description: "Third custom surcharge applied to the invoice" + type: number + example: 5.00 + custom_surcharge4: + description: "Fourth custom surcharge applied to the invoice" + type: number + example: 20.00 + type: object + RecurringQuote: + properties: + id: + description: 'The hashed id of the recurring quote' + type: string + example: Opnel5aKBz + user_id: + description: 'The user hashed id' + type: string + example: Opnel5aKBz + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: Opnel5aKBz + client_id: + description: 'The client hashed id' + type: string + example: Opnel5aKBz + status_id: + description: 'The quote status variable' + type: string + example: '4' + frequency_id: + description: 'The recurring quote frequency' + type: number + example: '4' + remaining_cycles: + description: 'The number of quotes left to be generated' + type: number + example: '4' + number: + description: 'The recurringquote number - is a unique alpha numeric number per quote per company' + type: string + example: INV_101 + po_number: + description: 'The purchase order associated with this recurring quote' + type: string + example: PO-1234 + terms: + description: 'The quote terms' + type: string + example: 'These are quote terms' + public_notes: + description: 'The public notes of the quote' + type: string + example: 'These are some public notes' + private_notes: + description: 'The private notes of the quote' + type: string + example: 'These are some private notes' + footer: + description: 'The quote footer notes' + type: string + example: '' + custom_value1: + description: 'A custom field value' + type: string + example: '2022-10-01' + custom_value2: + description: 'A custom field value' + type: string + example: 'Something custom' + custom_value3: + description: 'A custom field value' + type: string + example: '' + custom_value4: + description: 'A custom field value' + type: string + example: '' + tax_name1: + description: 'The tax name' + type: string + example: '' + tax_name2: + description: 'The tax name' + type: string + example: '' + tax_rate1: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_rate2: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_name3: + description: 'The tax name' + type: string + example: '' + tax_rate3: + description: 'The tax rate' + type: number + format: float + example: '10.00' + total_taxes: + description: 'The total taxes for the quote' + type: number + format: float + example: '10.00' + line_items: + description: 'An array of objects which define the line items of the quote' + type: object + example: '' + amount: + description: 'The quote amount' + type: number + format: float + example: '10.00' + balance: + description: 'The quote balance' + type: number + format: float + example: '10.00' + paid_to_date: + description: 'The amount paid on the quote to date' + type: number + format: float + example: '10.00' + discount: + description: 'The quote discount, can be an amount or a percentage' + type: number + format: float + example: '10.00' + partial: + description: 'The deposit/partial amount' + type: number + format: float + example: '10.00' + is_amount_discount: + description: 'Flag determining if the discount is an amount or a percentage' + type: boolean + example: true + is_deleted: + description: 'Defines if the quote has been deleted' + type: boolean + example: true + uses_inclusive_taxes: + description: 'Defines the type of taxes used as either inclusive or exclusive' + type: boolean + example: true + date: + description: 'The quote Date' + type: string + format: date + example: '1994-07-30' + last_sent_date: + description: 'The last date the quote was sent out' + type: string + format: date + example: '1994-07-30' + next_send_date: + description: 'The Next date for a reminder to be sent' + type: string + format: date + example: '1994-07-30' + partial_due_date: + description: 'The due date for the deposit/partial amount' + type: string + format: date + example: '1994-07-30' + due_date: + description: 'The due date of the quote' + type: string + format: date + example: '1994-07-30' + settings: + $ref: '#/components/schemas/CompanySettings' + last_viewed: + description: Timestamp + type: number + format: integer + example: '1434342123' + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + custom_surcharge1: + description: 'First Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge2: + description: 'Second Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge3: + description: 'Third Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge4: + description: 'Fourth Custom Surcharge' + type: number + format: float + example: '10.00' + custom_surcharge_tax1: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax2: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax3: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + custom_surcharge_tax4: + description: 'Toggles charging taxes on custom surcharge amounts' + type: boolean + example: true + type: object + Paymentable: + properties: + id: + description: 'The paymentable hashed id' + type: string + example: AS3df3A + invoice_id: + description: 'The invoice hashed id' + type: string + example: AS3df3A + credit_id: + description: 'The credit hashed id' + type: string + example: AS3df3A + refunded: + description: 'The amount that has been refunded for this payment' + type: number + format: float + example: '10.00' + amount: + description: 'The amount that has been applied to the payment' type: number format: float example: '10.00' @@ -17134,6 +17277,974 @@ components: format: integer example: '1434342123' type: object + Meta: + properties: + pagination: + $ref: '#/components/schemas/Pagination' + Pagination: + type: object + properties: + total: + type: integer + description: 'The total number of items' + example: 1 + readOnly: true + count: + type: integer + description: 'The number of items per page' + example: 1 + readOnly: true + per_page: + type: integer + description: 'The number of items per page' + example: 1 + readOnly: true + current_page: + type: integer + description: 'The current page number' + example: 1 + readOnly: true + total_pages: + type: integer + description: 'The total number of pages' + example: 1 + readOnly: true + links: + type: object + description: 'The pagination links' + readOnly: true + Project: + type: object + properties: + id: + description: 'The project hashed id' + type: string + example: Opnel5aKBz + user_id: + description: 'The user hashed id' + type: string + example: Opnel5aKBz + assigned_user_id: + description: The assigned user identifier associated with the project + type: string + example: Opnel5aKBz + client_id: + type: string + example: Opnel5aKBz + description: The client identifier associated with the project + name: + type: string + description: The name of the project + example: 'New Project' + task_rate: + type: number + format: float + example: 10 + description: The default rate per task for the project + due_date: + type: string + format: date + example: '2019-01-01' + description: The due date for the project + private_notes: + type: string + description: Private notes associated with the project + budgeted_hours: + type: number + format: float + description: The number of budgeted hours for the project + custom_value1: + type: string + description: Custom value field 1 + custom_value2: + type: string + description: Custom value field 2 + custom_value3: + type: string + description: Custom value field 3 + custom_value4: + type: string + description: Custom value field 4 + created_at: + type: number + format: integer + example: 134341234234 + description: The timestamp of the project creation + updated_at: + type: number + format: integer + example: 134341234234 + description: The timestamp of the last project update + archived_at: + type: number + format: integer + example: 134341234234 + description: The timestamp of the project deletion + public_notes: + type: string + description: Public notes associated with the project + is_deleted: + type: boolean + description: A flag indicating if the project is deleted + number: + type: string + description: The project number + color: + type: string + description: The color associated with the project + required: + - id + - user_id + - name + - task_rate + - budgeted_hours + - is_deleted + - color + + Client: + properties: + id: + description: 'The unique identifier of the client' + type: string + example: Opnel5aKBz + readOnly: true + contacts: + type: array + items: + $ref: '#/components/schemas/ClientContact' + user_id: + description: 'The unique identifier of the user who created the client' + type: string + example: Ua6Rw4pVbS + readOnly: true + assigned_user_id: + description: 'The unique identifier of the user who has been assigned the client' + type: string + example: Ua6Rw4pVbS + name: + description: 'The name of the client company or organization' + type: string + example: "Jim's Housekeeping" + website: + description: 'The website URL of the client company or organization' + type: string + example: 'https://www.jims-housekeeping.com' + private_notes: + description: 'Notes that are only visible to the user who created the client' + type: string + example: 'Client prefers email communication over phone calls' + client_hash: + description: 'A unique hash value for the client' + type: string + example: asdfkjhk342hjhbfdvmnfb1 + readOnly: true + industry_id: + description: 'The unique identifier of the industry the client operates in' + type: number + example: '5' + size_id: + description: 'The unique identifier for the size category of the client company or organization' + type: number + example: '2' + address1: + description: "First line of the client's address" + type: string + example: '123 Main St' + address2: + description: "Second line of the client's address, if needed" + type: string + example: 'Apt 4B' + city: + description: 'The city the client is located in' + type: string + example: 'Beverly Hills' + state: + description: 'The state, province, or locality the client is located in' + type: string + example: 'California' + postal_code: + description: 'The postal code or ZIP code of the client' + type: string + example: '90210' + phone: + description: "The client's phone number" + type: string + example: '555-3434-3434' + country_id: + description: "The unique identifier of the client's country" + type: number + format: integer + example: '1' + custom_value1: + description: 'A custom field for storing additional information' + type: string + example: 'Preferred contact: Email' + custom_value2: + description: 'A custom field for storing additional information' + type: string + example: 'Account manager: John Doe' + custom_value3: + description: 'A custom field for storing additional information' + type: string + example: 'VIP client: Yes' + custom_value4: + description: 'A custom field for storing additional information' + type: string + example: 'Annual contract value: $50,000' + vat_number: + description: "The client's VAT (Value Added Tax) number, if applicable" + type: string + example: 'VAT123456' + id_number: + description: 'A unique identification number for the client, such as a tax ID or business registration number' + type: string + number: + description: 'A system-assigned unique number for the client, typically used for invoicing purposes' + type: string + example: 'CL-0001' + shipping_address1: + description: "First line of the client's shipping address" + type: string + example: '5 Wallaby Way' + shipping_address2: + description: "Second line of the client's shipping address, if needed" + type: string + example: 'Suite 5' + shipping_city: + description: "The city of the client's shipping address" + type: string + example: 'Perth' + shipping_state: + description: "The state, province, or locality of the client's shipping address" + type: string + example: 'Western Australia' + shipping_postal_code: + description: "The postal code or ZIP code of the client's shipping address" + type: string + example: '6110' + shipping_country_id: + description: "The unique identifier of the country for the client's shipping address" + type: number + format: integer + example: '4' + is_deleted: + description: 'A boolean value indicating whether the client has been deleted or not' + type: boolean + example: false + readOnly: true + balance: + description: 'The outstanding balance the client owes' + type: number + format: float + example: '500.00' + readOnly: true + paid_to_date: + description: 'The total amount the client has paid to date' + type: number + format: float + example: '2000.00' + readOnly: true + credit_balance: + description: 'The available credit balance for the client to use on future purchases' + type: number + format: float + example: '100.00' + readOnly: true + last_login: + description: "The timestamp of the client's last login" + type: number + format: integer + example: '1628686031' + readOnly: true + created_at: + description: 'The timestamp when the client was created' + type: number + format: integer + example: '1617629031' + readOnly: true + updated_at: + description: 'The timestamp when the client was last updated' + type: number + format: integer + example: '1628445631' + readOnly: true + group_settings_id: + description: 'The group settings assigned to the client' + type: string + example: Opnel5aKBz + routing_id: + description: 'The routing address id for e-invoicing for this client' + type: string + example: Opnel5aKBz3489-dfkiu-2239-sdsd + is_tax_exempt: + description: 'Flag which defines if the client is exempt from taxes' + type: boolean + example: false + has_valid_vat_number: + description: 'Flag which defines if the client has a valid VAT number' + type: boolean + example: false + readOnly: true + payment_balance: + description: 'Defines the payment balance the client has on file (pre payments / over payments / unapplied amounts)' + type: number + example: 100 + readOnly: true + settings: + $ref: '#/components/schemas/ClientSettings' + type: object + Vendor: + properties: + id: + description: 'The hashed id of the vendor. This is a unique identifier for the vendor.' + type: string + example: Opnel5aKBz + readOnly: true + user_id: + description: 'The hashed id of the user who created the vendor. This is a unique identifier for the user.' + type: string + example: Opnel5aKBz + assigned_user_id: + description: 'The hashed id of the assigned user to this vendor. This is a unique identifier for the user.' + type: string + example: Opnel5aKBz + contacts: + type: array + items: + $ref: '#/components/schemas/VendorContact' + description: 'An array of contacts associated with the vendor.' + name: + description: 'The name of the vendor.' + type: string + example: 'Harry cafe de wheels' + classification: + description: 'The classification of the vendor.' + type: string + example: 'individual' + website: + description: 'The website of the vendor.' + type: string + example: www.harry.com + private_notes: + description: 'The private notes of the vendor. These notes are only visible to users with appropriate permissions.' + type: string + example: 'Shhh, do not tell the vendor' + industry_id: + description: 'The industry id of the vendor. This is a unique identifier for the industry.' + type: string + example: '1' + size_id: + description: 'The size id of the vendor. This is a unique identifier for the size of the vendor.' + type: string + example: '' + address1: + description: 'The first line of the vendor''s address.' + type: string + example: '' + address2: + description: 'The second line of the vendor''s address.' + type: string + example: '' + city: + description: 'The city of the vendor''s address.' + type: string + example: '' + state: + description: 'The state of the vendor''s address.' + type: string + example: '' + postal_code: + description: 'The postal code of the vendor''s address.' + type: string + example: '' + phone: + description: 'The phone number of the vendor.' + type: string + example: 555-3434-3434 + country_id: + description: 'The country id of the vendor. This is a unique identifier for the country.' + type: string + example: '' + currency_id: + description: 'The currency id of the vendor. This is a unique identifier for the currency.' + type: string + example: '4' + custom_value1: + description: 'The value of the first custom field for the vendor.' + type: string + example: '' + custom_value2: + description: 'The value of the second custom field for the vendor.' + type: string + example: '' + custom_value3: + description: 'The value of the third custom field for the vendor.' + type: string + example: '' + custom_value4: + description: 'The value of the fourth custom field for the vendor.' + type: string + example: '' + vat_number: + description: 'The VAT number of the vendor.' + type: string + example: '' + id_number: + description: 'The ID number of the vendor.' + type: string + example: '' + number: + description: 'The number of the vendor' + type: string + example: '11234' + is_deleted: + description: 'Boolean flag determining if the vendor has been deleted' + type: boolean + example: true + language_id: + description: 'The language id of the vendor. This is a unique identifier for the language.' + type: string + example: '1' + vendor_hash: + description: 'The vendor hash of the vendor. This is a unique identifier for the vendor.' + type: string + example: 'aaa-sss-www' + readOnly: true + transaction_name: + description: 'The transaction name of the vendor.' + type: string + example: 'aaa-sss-www' + last_login: + description: Timestamp + type: number + format: integer + example: '134341234234' + readOnly: true + created_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + readOnly: true + updated_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + readOnly: true + display_name: + description: 'The display name of the vendor.' + type: string + example: 'Bob the vendor' + readOnly: true + type: object + VendorContact: + properties: + id: + description: 'The hashed id of the vendor contact' + type: string + example: Opnel5aKBz + readOnly: true + user_id: + description: 'The hashed id of the user id' + type: string + example: Opnel5aKBz + readOnly: true + vendor_id: + description: 'The hashed id of the vendor' + type: string + example: Opnel5aKBz + readOnly: true + first_name: + description: 'The first name of the contact' + type: string + example: Harry + last_name: + description: 'The last name of the contact' + type: string + example: Windsor + contact_key: + description: 'A unique identifier for the contact' + type: string + example: JD0X52bkfZlJRiroCJ0tcSiAjsJTntZ5uqKdiZ0a + readOnly: true + confirmation_code: + description: 'The confirmation code used to authenticate the contacts email address' + type: string + example: 333-sdjkh34gbasd + readOnly: true + phone: + description: 'The contacts phone number' + type: string + example: 555-123-1234 + custom_value1: + description: 'A custom value' + type: string + example: '2022-10-10' + custom_value2: + description: 'A custom value' + type: string + example: $1000 + custom_value3: + description: 'A custom value' + type: string + example: '' + custom_value4: + description: 'A custom value' + type: string + example: '' + email: + description: 'The contact email address' + type: string + example: harry@windsor.com + email_verified_at: + description: 'The date which the contact confirmed their email' + type: number + format: integer + example: '134341234234' + readOnly: true + password: + description: 'The hashed password of the contact' + type: string + example: '*****' + is_primary: + description: 'Boolean flag determining if the contact is the primary contact for the vendor' + type: boolean + example: true + created_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + readOnly: true + updated_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + readOnly: true + deleted_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + readOnly: true + type: object + Quote: + properties: + id: + description: 'The unique hashed identifier for the quote' + type: string + example: Opnel5aKBz + user_id: + description: 'The unique hashed identifier for the user who created the quote' + type: string + example: '' + assigned_user_id: + description: 'The unique hashed identifier for the user assigned to the quote' + type: string + example: '' + client_id: + description: 'The unique hashed identifier for the client associated with the quote' + type: string + example: '' + status_id: + description: 'The status of the quote represented by a unique identifier' + type: string + example: '' + number: + description: 'The unique alpha-numeric quote number for the quote per company' + type: string + example: QUOTE_101 + po_number: + description: 'The purchase order number associated with the quote' + type: string + example: PO-1234 + terms: + description: 'The terms and conditions for the quote' + type: string + example: 'These are some quote terms. Valid for 14 days.' + public_notes: + description: 'Publicly visible notes associated with the quote' + type: string + example: 'These are public notes which the client may see' + private_notes: + description: 'Privately visible notes associated with the quote, not disclosed to the client' + type: string + example: 'These are private notes, not to be disclosed to the client' + footer: + description: 'The footer text of the quote' + type: string + example: 'The text goes in the footer of the quote' + custom_value1: + description: 'First custom value field for additional information' + type: string + example: 'A custom value' + custom_value2: + description: 'Second custom value field for additional information' + type: string + example: 'A custom value' + custom_value3: + description: 'Third custom value field for additional information' + type: string + example: 'A custom value' + custom_value4: + description: 'Fourth custom value field for additional information' + type: string + example: 'A custom value' + tax_name1: + description: 'The name of the first tax applied to the quote' + type: string + example: GST + tax_name2: + description: 'The name of the second tax applied to the quote' + type: string + example: VAT + tax_rate1: + description: 'The rate of the first tax applied to the quote' + type: number + format: float + example: 10.00 + tax_rate2: + description: 'The rate of the second tax applied to the quote' + type: number + format: float + example: 10.00 + tax_name3: + description: 'The name of the third tax applied to the quote' + type: string + example: '' + tax_rate3: + description: 'The rate of the third tax applied to the quote' + type: number + format: float + example: 10.00 + total_taxes: + description: 'The total amount of taxes for the quote' + type: number + format: float + example: 10.00 + line_items: + type: array + description: 'An array of objects which define the line items of the quote' + items: + $ref: '#/components/schemas/InvoiceItem' + amount: + description: 'The total amount of the quote before taxes and discounts' + type: number + format: float + example: 10.00 + balance: + description: 'The balance due for the quote after accounting for payments' + type: number + format: float + example: 10.00 + paid_to_date: + description: 'The total amount paid on the quote so far' + type: number + format: float + example: 10.00 + discount: + description: 'The discount amount or percentage applied to the quote' + type: number + format: float + example: 10.00 + partial: + description: 'The partial or deposit amount for the quote' + type: number + format: float + example: 10.00 + is_amount_discount: + description: 'Boolean flag indicating if the discount is a fixed amount or a percentage' + type: boolean + example: true + is_deleted: + description: 'Boolean flag indicating if the quote has been deleted' + type: boolean + example: false + uses_inclusive_taxes: + description: 'Boolean flag indicating if the taxes used are inclusive or exclusive' + type: boolean + example: true + date: + description: 'The date the quote was created' + type: string + format: date + example: '1994-07-30' + last_sent_date: + description: 'The last date the quote was sent to the client' + type: string + format: date + example: '1994-07-30' + next_send_date: + description: 'The next scheduled date for sending a reminder for the quote' + type: string + format: date + example: '1994-07-30' + partial_due_date: + description: 'The due date for the partial or deposit amount' + type: string + format: date + example: '1994-07-30' + due_date: + description: 'The due date for the total amount of the quote' + type: string + format: date + example: '1994-07-30' + settings: + $ref: '#/components/schemas/CompanySettings' + last_viewed: + description: 'The timestamp of the last time the quote was viewed' + type: number + format: integer + example: 1434342123 + updated_at: + description: 'The timestamp of the last update to the quote' + type: number + format: integer + example: 1434342123 + archived_at: + description: 'The timestamp of when the quote was archived' + type: number + format: integer + example: 1434342123 + custom_surcharge1: + description: 'First custom surcharge amount for the quote' + type: number + format: float + example: 10.00 + custom_surcharge2: + description: 'Second custom surcharge amount for the quote' + type: number + format: float + example: 10.00 + custom_surcharge3: + description: 'Third custom surcharge amount for the quote' + type: number + format: float + example: 10.00 + custom_surcharge4: + description: 'Fourth custom surcharge amount for the quote' + type: number + format: float + example: 10.00 + custom_surcharge_tax1: + description: 'Boolean flag indicating if taxes are charged on the first custom surcharge amount' + type: boolean + example: true + custom_surcharge_tax2: + description: 'Boolean flag indicating if taxes are charged on the second custom surcharge amount' + type: boolean + example: true + custom_surcharge_tax3: + description: 'Boolean flag indicating if taxes are charged on the third custom surcharge amount' + type: boolean + example: true + custom_surcharge_tax4: + description: 'Boolean flag indicating if taxes are charged on the fourth custom surcharge amount' + type: boolean + example: true + type: object + Payment: + properties: + id: + description: 'The payment hashed id' + type: string + example: Opnel5aKBz + client_id: + description: 'The client hashed id' + type: string + example: Opnel5aKBz + invitation_id: + description: 'The invitation hashed id' + type: string + example: Opnel5aKBz + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + user_id: + description: 'The user hashed id' + type: string + example: Opnel5aKBz + type_id: + description: 'The Payment Type ID' + type: string + example: '1' + date: + description: 'The Payment date' + type: string + example: 1-1-2014 + transaction_reference: + description: 'The transaction reference as defined by the payment gateway' + type: string + example: xcsSxcs124asd + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: Opnel5aKBz + private_notes: + description: 'The private notes of the payment' + type: string + example: 'The payment was refunded due to error' + is_manual: + description: 'Flags whether the payment was made manually or processed via a gateway' + type: boolean + example: true + is_deleted: + description: 'Defines if the payment has been deleted' + type: boolean + example: true + amount: + description: 'The amount of this payment' + type: number + example: 10 + refunded: + description: 'The refunded amount of this payment' + type: number + example: 10 + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + company_gateway_id: + description: 'The company gateway id' + type: string + example: '3' + paymentables: + $ref: '#/components/schemas/Paymentable' + invoices: + description: '' + type: array + items: + $ref: '#/components/schemas/InvoicePaymentable' + credits: + description: '' + type: array + items: + $ref: '#/components/schemas/CreditPaymentable' + number: + description: 'The payment number - is a unique alpha numeric number per payment per company' + type: string + example: PAY_101 + type: object + + Task: + properties: + id: + description: 'The hashed id of the task' + type: string + example: Opnel5aKBz + user_id: + description: 'The hashed id of the user who created the task' + type: string + example: Opnel5aKBz + assigned_user_id: + description: 'The assigned user of the task' + type: string + example: Opnel5aKBz + client_id: + description: 'The hashed if of the client' + type: string + example: Opnel5aKBz + invoice_id: + description: 'The hashed id of the invoice associated with the task' + type: string + example: Opnel5aKBz + project_id: + description: 'The hashed id of the project associated with the task' + type: string + example: Opnel5aKBz + number: + description: 'The number of the task' + type: string + example: TASK-123 + time_log: + description: 'An array of unix time stamps defining the start and end times of the task' + type: string + example: '[[1,2],[3,4]]' + is_running: + description: 'Determines if the task is still running' + type: boolean + example: true + is_deleted: + description: 'Boolean flag determining if the task has been deleted' + type: boolean + example: true + task_status_id: + description: 'The hashed id of the task status' + type: string + example: Opnel5aKBz + description: + description: 'The task description' + type: string + example: 'A wonder task to work on' + duration: + description: 'The task duration in seconds' + type: integer + example: '3600' + task_status_order: + description: 'The order of the task' + type: integer + example: '4' + rate: + description: 'The task rate' + type: number + example: 10.00 + custom_value1: + description: 'A custom value' + type: string + example: '2022-10-10' + custom_value2: + description: 'A custom value' + type: string + example: $1100 + custom_value3: + description: 'A custom value' + type: string + example: 'I need help' + custom_value4: + description: 'A custom value' + type: string + example: INV-3343 + is_date_based: + description: 'Boolean flag determining if the task is date based' + type: boolean + example: true + calculated_start_date: + description: 'The calculated start date of the task' + type: string + example: '2022-10-10' + readOnly: true + invoice_documents: + description: "Boolean flags which determines whether to include the task documents on the invoice" + type: boolean + example: true + created_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + updated_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: Timestamp + type: number + format: integer + example: '1434342123' + readOnly: true + type: object RecurringInvoice: properties: id: @@ -17357,64 +18468,408 @@ components: example: true type: object - Payment: + InvoiceItem: + type: object + properties: + quantity: + type: number + example: 1 + description: 'The quantity of the product offered for this line item' + cost: + type: number + format: float + example: 10.00 + description: 'The cost of the product offered for this line item' + product_key: + type: string + example: 'Product key' + description: 'The product key of the product offered for this line item (Referred to as Product in the product tab)' + product_cost: + type: number + format: float + example: 10.00 + description: 'The cost of the product offered for this line item (Referred to as Cost in the product tab)' + notes: + type: string + example: 'Item notes' + description: 'The notes/description for the product offered for this line item' + discount: + type: number + format: float + example: 5.00 + description: 'The discount applied to the product offered for this line item' + is_amount_discount: + type: boolean + example: false + description: 'Indicates whether the discount applied to the product offered for this line item is a fixed amount or a percentage' + tax_name1: + type: string + example: 'GST' + description: 'The name of the first tax applied to the product offered for this line item' + tax_rate1: + type: number + format: float + example: 10.00 + description: 'The rate of the first tax applied to the product offered for this line item' + tax_name2: + type: string + example: 'VAT' + description: 'The name of the second tax applied to the product offered for this line item' + tax_rate2: + type: number + format: float + example: 5.00 + description: 'The rate of the second tax applied to the product offered for this line item' + tax_name3: + type: string + example: 'CA Sales Tax' + description: 'The name of the third tax applied to the product offered for this line item' + tax_rate3: + type: number + format: float + example: 3.00 + description: 'The rate of the third tax applied to the product offered for this line item' + sort_id: + type: string + example: '0' + description: 'Deprecated' + deprecated: true + line_total: + type: number + format: float + example: 10.00 + description: 'The total amount of the product offered for this line item' + readOnly: true + gross_line_total: + type: number + format: float + example: 15.00 + description: 'The total amount of the product offered for this line item before discounts' + readOnly: true + tax_amount: + type: number + format: float + example: 1.00 + description: 'The total amount of tax applied to the product offered for this line item' + readOnly: true + date: + type: string + format: date-time + example: '2023-03-19T00:00:00Z' + description: 'Deprecated' + deprecated: true + custom_value1: + type: string + example: 'Custom value 1' + description: 'The first custom value of the product offered for this line item' + custom_value2: + type: string + example: 'Custom value 2' + description: 'The second custom value of the product offered for this line item' + custom_value3: + type: string + example: 'Custom value 3' + description: 'The third custom value of the product offered for this line item' + custom_value4: + type: string + example: 'Custom value 4' + description: 'The fourth custom value of the product offered for this line item' + type_id: + type: string + example: '1' + description: '1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense' + default: '1' + tax_id: + type: string + example: '1' + default: '1' + description: 'The tax ID of the product: 1 product, 2 service, 3 digital, 4 shipping, 5 exempt, 5 reduced tax, 7 override, 8 zero rate, 9 reverse tax' + Activity: properties: id: - description: 'The payment hashed id' + description: 'The id field of the activity' + type: string + example: Opnel5aKBz + activity_type_id: + description: 'The activity type id' type: string example: Opnel5aKBz client_id: description: 'The client hashed id' type: string example: Opnel5aKBz - invitation_id: - description: 'The invitation hashed id' - type: string - example: Opnel5aKBz - client_contact_id: - description: 'The client contact hashed id' + company_id: + description: 'The company hashed id' type: string example: Opnel5aKBz user_id: description: 'The user hashed id' type: string example: Opnel5aKBz - type_id: - description: 'The Payment Type ID' - type: string - example: '1' - date: - description: 'The Payment date' - type: string - example: 1-1-2014 - transaction_reference: - description: 'The transaction reference as defined by the payment gateway' - type: string - example: xcsSxcs124asd - assigned_user_id: - description: 'The assigned user hashed id' + invoice_id: + description: 'The invoice hashed id' type: string example: Opnel5aKBz - private_notes: - description: 'The private notes of the payment' + payment_id: + description: 'The payment hashed id' type: string - example: 'The payment was refunded due to error' - is_manual: - description: 'Flags whether the payment was made manually or processed via a gateway' + example: Opnel5aKBz + credit_id: + description: 'The credit hashed id' + type: string + example: Opnel5aKBz + updated_at: + description: 'Unixtimestamp the last time the record was updated' + type: integer + example: '343421434' + expense_id: + description: 'The expense hashed id' + type: string + example: Opnel5aKBz + is_system: + description: 'Defines is the activity was performed by the system' + type: boolean + example: true + contact_id: + description: 'The contact hashed id' + type: string + example: Opnel5aKBz + task_id: + description: 'The task hashed id' + type: string + example: Opnel5aKBz + notes: + description: 'Activity Notes' + type: string + example: Opnel5aKBz + token_id: + description: 'The hashed ID of the token who performed the action' + type: string + example: Opnel5aKBz + ip: + description: 'The IP Address of the user who performed the action' + type: string + example: 192.168.1.252 + user: + $ref: '#/components/schemas/User' + client: + $ref: '#/components/schemas/Client' + contact: + $ref: '#/components/schemas/ClientContact' + recurring_invoice: + $ref: '#/components/schemas/RecurringInvoice' + invoice: + $ref: '#/components/schemas/Invoice' + credit: + $ref: '#/components/schemas/Credit' + quote: + $ref: '#/components/schemas/Quote' + payment: + $ref: '#/components/schemas/Payment' + expense: + $ref: '#/components/schemas/Expense' + task: + $ref: '#/components/schemas/Task' + purchase_order: + $ref: '#/components/schemas/PurchaseOrder' + vendor: + $ref: '#/components/schemas/Vendor' + vendor_contact: + $ref: '#/components/schemas/VendorContact' + type: object + + ProductBulkAction: + required: + - action + - ids + properties: + action: + type: string + example: archive + description: 'The action to perform ie. archive / restore / delete / set_tax_id' + ids: + type: array + items: + format: string + type: string + example: 2J234DFA,D2J234DFA,D2J234DFA + description: string array of client hashed ids + tax_id: + type: string + example: '1' + description: | + The tax rate id to set on the list of products + + The following constants are available (default = '1') + + ``` + PRODUCT_TYPE_PHYSICAL = '1' + PRODUCT_TYPE_SERVICE = '2' + PRODUCT_TYPE_DIGITAL = '3' + PRODUCT_TYPE_SHIPPING = '4' + PRODUCT_TYPE_EXEMPT = '5' + PRODUCT_TYPE_REDUCED_TAX = '6' + PRODUCT_TYPE_OVERRIDE_TAX = '7' + PRODUCT_TYPE_ZERO_RATED = '8' + PRODUCT_TYPE_REVERSE_TAX = '9' + ``` + type: object + RecurringExpense: + properties: + id: + description: 'The hashed id of the recurring expense' + type: string + example: Opnel5aKBz + user_id: + description: 'The hashed id of the user who created the recurring expense' + type: string + example: Opnel5aKBz + assigned_user_id: + description: 'The hashed id of the user assigned to this recurring expense' + type: string + example: Opnel5aKBz + client_id: + description: 'The hashed id of the client' + type: string + example: Opnel5aKBz + invoice_id: + description: 'The hashed id of the invoice' + type: string + example: Opnel5aKBz + bank_id: + description: 'The id of the bank associated with this recurring expense' + type: string + example: '22' + invoice_currency_id: + description: 'The currency id of the invoice associated with this recurring expense' + type: string + example: '1' + expense_currency_id: + description: 'The currency id of the expense associated with this recurring expense' + type: string + example: '1' + invoice_category_id: + description: 'The category id of the invoice' + type: string + example: '1' + payment_type_id: + description: 'The payment type id' + type: string + example: '1' + private_notes: + description: 'The recurring expense private notes' + type: string + example: 'Private and confidential' + public_notes: + description: 'The recurring expense public notes' + type: string + example: 'This is the best client in the world' + transaction_reference: + description: 'The recurring expense transaction reference' + type: string + example: EXP-1223-2333 + transcation_id: + description: 'The transaction id of the recurring expense' + type: string + example: '1233312312' + custom_value1: + description: 'Custom value field' + type: string + example: $1000 + custom_value2: + description: 'Custom value field' + type: string + example: '2022-10-10' + custom_value3: + description: 'Custom value field' + type: string + example: 'short text' + custom_value4: + description: 'Custom value field' + type: string + example: 'very long text' + tax_name1: + description: 'The tax name' + type: string + example: GST + tax_name2: + description: 'The tax name' + type: string + example: VAT + tax_rate1: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_rate2: + description: 'The tax rate' + type: number + format: float + example: '10.00' + tax_name3: + description: 'The tax name' + type: string + example: '' + tax_rate3: + description: 'The tax rate' + type: number + format: float + example: '10.00' + amount: + description: 'The total amount of the recurring expense' + type: number + format: float + example: '10.00' + frequency_id: + description: 'The frequency this recurring expense fires' + type: number + format: int + example: '1' + remaining_cycles: + description: 'The number of remaining cycles for this recurring expense' + type: number + format: int + example: '1' + foreign_amount: + description: 'The foreign currency amount of the recurring expense' + type: number + format: float + example: '10.00' + exchange_rate: + description: 'The exchange rate for the expernse' + type: number + format: float + example: '0.80' + date: + description: 'The date of the expense' + type: string + example: '' + payment_date: + description: 'The date the expense was paid' + type: string + example: '' + should_be_invoiced: + description: 'Boolean flag determining if the expense should be invoiced' type: boolean example: true is_deleted: - description: 'Defines if the payment has been deleted' + description: 'Boolean flag determining if the recurring expense is deleted' + type: boolean + example: true + last_sent_date: + description: 'The Date it was sent last' + type: string + format: date + example: '1994-07-30' + next_send_date: + description: 'The next send date' + type: string + format: date + example: '1994-07-30' + invoice_documents: + description: 'Boolean flag determining if the documents associated with this expense should be passed onto the invoice if it is converted to an invoice' type: boolean example: true - amount: - description: 'The amount of this payment' - type: number - example: 10 - refunded: - description: 'The refunded amount of this payment' - type: number - example: 10 updated_at: description: Timestamp type: number @@ -17425,442 +18880,1801 @@ components: type: number format: integer example: '1434342123' - company_gateway_id: - description: 'The company gateway id' - type: string - example: '3' - paymentables: - $ref: '#/components/schemas/Paymentable' - invoices: - description: '' - type: array - items: - $ref: '#/components/schemas/InvoicePaymentable' - credits: - description: '' - type: array - items: - $ref: '#/components/schemas/CreditPaymentable' - number: - description: 'The payment number - is a unique alpha numeric number per payment per company' - type: string - example: PAY_101 type: object - - Credit: + PaymentTerm: + properties: + num_days: + description: 'The payment term length in days' + type: integer + example: '1' + name: + description: 'The payment term length in string format' + type: string + example: 'NET 1' + created_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + updated_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + archived_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + type: object + ClientGatewayToken: properties: id: - description: "The unique hashed ID of the credit" + description: 'The hashed id of the client gateway token' + type: string + example: Opnel5aKBz + client_id: + description: 'The hashed_id of the client' + type: string + example: '2' + token: + description: 'The payment token' + type: string + example: '2' + routing_number: + description: 'THe bank account routing number' + type: string + example: '2' + company_gateway_id: + description: 'The hashed id of the company gateway' + type: string + example: '2' + is_default: + description: 'Flag determining if the token is the default payment method' + type: boolean + example: 'true' + type: object + User: + properties: + id: + description: 'The hashed id of the user' + type: string + example: Opnel5aKBz + readOnly: true + first_name: + description: 'The first name of the user' + type: string + example: Brad + last_name: + description: 'The last name of the user' + type: string + example: Pitt + email: + description: 'The users email address' + type: string + example: brad@pitt.com + phone: + description: 'The users phone number' + type: string + example: 555-1233-23232 + signature: + description: 'The users sign off signature' + type: string + example: 'Have a nice day!' + avatar: + description: 'The users avatar' + type: string + example: 'https://url.to.your/avatar.png' + accepted_terms_version: + description: 'The version of the invoice ninja terms that has been accepted by the user' + type: string + example: 1.0.1 + readOnly: true + oauth_user_id: + description: 'The provider id of the oauth entity' + type: string + example: jkhasdf789as6f675sdf768sdfs + readOnly: true + oauth_provider_id: + description: 'The oauth entity id' + type: string + example: google + readOnly: true + language_id: + description: 'The language id of the user' + type: string + example: 1 + verified_phone_number: + description: 'Boolean flag if the user has their phone verified. Required to settings up 2FA' + type: boolean + example: true + readOnly: true + sms_verification_code: + description: 'The sms verification code for the user. Required to settings up 2FA' + type: string + example: '123456' + readOnly: true + oauth_user_token_expiry: + description: 'The expiry date of the oauth token' + type: string + example: '2022-10-10' + readOnly: true + has_password: + description: 'Boolean flag determining if the user has a password' + type: boolean + example: true + readOnly: true + last_confirmed_email_address: + description: 'The last confirmed email address of the user' + type: string + example: 'bob@gmail.com' + readOnly: true + custom_value1: + description: 'A custom value' + type: string + example: 'Custom value 1' + custom_value2: + description: 'A custom value' + type: string + example: '$1000' + custom_value3: + description: 'A custom value' + type: string + example: 'Custom value 3' + custom_value4: + description: 'A custom value' + type: string + example: 'Custom value 4' + is_deleted: + description: 'Boolean flag determining if the user has been deleted' + type: boolean + example: true + readOnly: true + google_2fa_secret: + description: 'The google 2fa secret for the user' + type: string + example: '123456' + readOnly: true + company_user: + $ref: '#/components/schemas/CompanyUserRef' + type: object + UserRef: + properties: + id: + description: 'The hashed id of the user' + type: string + example: Opnel5aKBz + readOnly: true + first_name: + description: 'The first name of the user' + type: string + example: Brad + last_name: + description: 'The last name of the user' + type: string + example: Pitt + email: + description: 'The users email address' + type: string + example: brad@pitt.com + phone: + description: 'The users phone number' + type: string + example: 555-1233-23232 + signature: + description: 'The users sign off signature' + type: string + example: 'Have a nice day!' + avatar: + description: 'The users avatar' + type: string + example: 'https://url.to.your/avatar.png' + accepted_terms_version: + description: 'The version of the invoice ninja terms that has been accepted by the user' + type: string + example: 1.0.1 + readOnly: true + oauth_user_id: + description: 'The provider id of the oauth entity' + type: string + example: jkhasdf789as6f675sdf768sdfs + readOnly: true + oauth_provider_id: + description: 'The oauth entity id' + type: string + example: google + readOnly: true + language_id: + description: 'The language id of the user' + type: string + example: 1 + verified_phone_number: + description: 'Boolean flag if the user has their phone verified. Required to settings up 2FA' + type: boolean + example: true + readOnly: true + sms_verification_code: + description: 'The sms verification code for the user. Required to settings up 2FA' + type: string + example: '123456' + readOnly: true + oauth_user_token_expiry: + description: 'The expiry date of the oauth token' + type: string + example: '2022-10-10' + readOnly: true + has_password: + description: 'Boolean flag determining if the user has a password' + type: boolean + example: true + readOnly: true + last_confirmed_email_address: + description: 'The last confirmed email address of the user' + type: string + example: 'bob@gmail.com' + readOnly: true + custom_value1: + description: 'A custom value' + type: string + example: 'Custom value 1' + custom_value2: + description: 'A custom value' + type: string + example: '$1000' + custom_value3: + description: 'A custom value' + type: string + example: 'Custom value 3' + custom_value4: + description: 'A custom value' + type: string + example: 'Custom value 4' + is_deleted: + description: 'Boolean flag determining if the user has been deleted' + type: boolean + example: true + readOnly: true + google_2fa_secret: + description: 'The google 2fa secret for the user' + type: string + example: '123456' + readOnly: true + type: object + CompanyGateway: + properties: + id: + description: 'The hashed id of the company gateway' + type: string + example: Opnel5aKBz + gateway_key: + description: 'The gateway key (hash)' + type: string + example: '2' + accepted_credit_cards: + description: 'Bitmask representation of cards' + type: integer + example: '32' + require_billing_address: + description: 'Determines if the the billing address is required prior to payment.' + type: boolean + example: true + require_shipping_address: + description: 'Determines if the the billing address is required prior to payment.' + type: boolean + example: true + config: + description: 'The configuration map for the gateway' + type: string + example: dfadsfdsafsafd + update_details: + description: 'Determines if the client details should be updated.' + type: boolean + example: true + fees_and_limits: + description: 'A mapped collection of the fees and limits for the configured gateway' + type: array + items: + $ref: '#/components/schemas/FeesAndLimits' + type: object + + + Document: + properties: + id: + description: 'The document hashed id' + type: string + example: AS3df3A + user_id: + description: 'The user hashed id' + type: string + example: '' + assigned_user_id: + description: 'The assigned user hashed id' + type: string + example: '' + project_id: + description: 'The project associated with this document' + type: string + example: '' + vendor_id: + description: 'The vendor associated with this documents' + type: string + example: '' + name: + description: 'The document name' + type: string + example: Beauty + url: + description: 'The document url' + type: string + example: Beauty + preview: + description: 'The document preview url' + type: string + example: Beauty + type: + description: 'The document type' + type: string + example: Beauty + disk: + description: 'The document disk' + type: string + example: Beauty + hash: + description: 'The document hashed' + type: string + example: Beauty + is_deleted: + description: 'Flag to determine if the document is deleted' + type: boolean + example: true + is_default: + description: 'Flag to determine if the document is a default doc' + type: boolean + example: true + created_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + updated_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + deleted_at: + description: Timestamp + type: number + format: integer + example: '134341234234' + type: object + PurchaseOrder: + properties: + id: + description: 'The unique hashed identifier for the purchase order' type: string example: Opnel5aKBz user_id: - description: "The unique hashed ID of the user associated with the credit" + description: 'The unique hashed identifier for the user who created the purchase order' type: string - example: 1a2b3c4d5e + example: '' assigned_user_id: - description: "The unique hashed ID of the assigned user responsible for the credit" + description: 'The unique hashed identifier for the user assigned to the purchase order' type: string - example: 6f7g8h9i0j - client_id: - description: "The unique hashed ID of the client associated with the credit" + example: '' + vendor_id: + description: 'The unique hashed identifier for the vendor associated with the purchase order' type: string - example: p1q2r3s4t5 + example: '' status_id: - description: "The ID representing the current status of the credit" + description: 'The status of the purchase order represented by a unique identifier' type: string - example: 3 - invoice_id: - description: "The unique hashed ID of the linked invoice to which the credit is applied" - type: string - example: u1v2w3x4y5 + example: '' number: - description: "The unique alphanumeric credit number per company" + description: 'The unique alpha-numeric purchase order number per company' + type: string + example: PO_101 + quote_number: + description: 'The quote number associated with this purchase order' type: string example: QUOTE_101 - po_number: - description: "The purchase order number referred to by the credit" - type: string - example: PO_12345 terms: - description: "The terms associated with the credit" + description: 'The terms and conditions for the purchase order' type: string - example: "Net 30" + example: 'These are some purchase order terms. Valid for 14 days.' public_notes: - description: "Public notes for the credit" + description: 'Publicly visible notes associated with the purchase order' type: string - example: "Thank you for your business." + example: 'These are public notes which the vendor may see' private_notes: - description: "Private notes for internal use, not visible to the client" + description: 'Privately visible notes associated with the purchase order, not disclosed to the vendor' type: string - example: "Client is requesting a discount." + example: 'These are private notes, not to be disclosed to the vendor' footer: - description: "The footer text for the credit" + description: 'The footer text of the purchase order' type: string - example: "Footer text goes here." + example: 'The text goes in the footer of the purchase order' custom_value1: - description: "Custom value 1 for additional credit information" + description: 'First custom value field for additional information' type: string - example: "Custom data 1" + example: 'A custom value' custom_value2: - description: "Custom value 2 for additional credit information" + description: 'Second custom value field for additional information' type: string - example: "Custom data 2" + example: 'A custom value' custom_value3: - description: "Custom value 3 for additional credit information" + description: 'Third custom value field for additional information' type: string - example: "Custom data 3" + example: 'A custom value' custom_value4: - description: "Custom value 4 for additional credit information" + description: 'Fourth custom value field for additional information' type: string - example: "Custom data 4" + example: 'A custom value' tax_name1: - description: "The name of the first tax applied to the credit" + description: 'The name of the first tax applied to the purchase order' type: string - example: "VAT" + example: GST tax_name2: - description: "The name of the second tax applied to the credit" + description: 'The name of the second tax applied to the purchase order' type: string - example: "GST" + example: VAT tax_rate1: - description: "The rate of the first tax applied to the credit" + description: 'The rate of the first tax applied to the purchase order' type: number format: float example: 10.00 tax_rate2: - description: "The rate of the second tax applied to the credit" + description: 'The rate of the second tax applied to the purchase order' type: number format: float - example: 5.00 + example: 10.00 tax_name3: - description: "The name of the third tax applied to the credit" + description: 'The name of the third tax applied to the purchase order' type: string - example: "PST" + example: '' tax_rate3: - description: "The rate of the third tax applied to the credit" + description: 'The rate of the third tax applied to the purchase order' type: number format: float - example: 8.00 + example: 10.00 total_taxes: - description: "The total amount of taxes for the credit" + description: 'The total amount of taxes applied to the purchase order' type: number format: float - example: 23.00 + example: 10.00 line_items: type: array - description: 'An array of objects which define the line items of the credit' + description: 'An array of objects which define the line items of the purchase order' items: $ref: '#/components/schemas/InvoiceItem' amount: - description: "The total amount of the credit" + description: 'The total amount of the purchase order before taxes and discounts' type: number format: float - example: 100.00 + example: 10.00 balance: - description: "The outstanding balance of the credit" + description: 'The balance due for the purchase order after accounting for payments' type: number format: float - example: 50.00 + example: 10.00 paid_to_date: - description: "The total amount paid to date for the credit" + description: 'The total amount paid on the purchase order so far' type: number format: float - example: 50.00 + example: 10.00 discount: - description: "The discount applied to the credit" + description: 'The discount amount or percentage applied to the purchase order' type: number format: float example: 10.00 partial: - description: "The partial amount applied to the credit" + description: 'The partial or deposit amount for the purchase order' type: number format: float - example: 20.00 + example: 10.00 is_amount_discount: - description: "Indicates whether the discount applied is a fixed amount or a percentage" + description: 'Boolean flag indicating if the discount is a fixed amount or a percentage' type: boolean example: true is_deleted: - description: "Indicates whether the credit has been deleted" + description: 'Boolean flag indicating if the purchase order has been deleted' type: boolean example: false uses_inclusive_taxes: - description: "Indicates whether the tax rates applied to the credit are inclusive or exclusive" + description: 'Boolean flag indicating if the taxes used are inclusive or exclusive' type: boolean example: true date: - description: "The date the credit was issued" + description: 'The date the purchase order was created' type: string format: date - example: "1994-07-30" + example: '1994-07-30' last_sent_date: - description: "The date the credit was last sent out" + description: 'The last date the purchase order was sent to the vendor' type: string format: date - example: "1994-07-30" + example: '1994-07-30' next_send_date: - description: "The next scheduled date for sending a credit reminder" + description: 'The next scheduled date for sending a reminder for the purchase order' type: string format: date - example: "1994-07-30" + example: '1994-07-30' partial_due_date: - description: "The due date for the partial amount of the credit" + description: 'The due date for the partial or deposit amount' type: string format: date - example: "1994-07-30" + example: '1994-07-30' due_date: - description: "The due date for the total amount of the credit" + description: 'The due date for the total amount of the purchase order' type: string format: date - example: "1994-07-30" + example: '1994-07-30' settings: - $ref: "#/components/schemas/CompanySettings" + $ref: '#/components/schemas/CompanySettings' last_viewed: - description: "The timestamp of the last time the credit was viewed" + description: Timestamp type: number format: integer example: 1434342123 updated_at: - description: "The timestamp of the last time the credit was updated" + description: Timestamp type: number format: integer example: 1434342123 archived_at: - description: "The timestamp of the last time the credit was archived" + description: Timestamp type: number format: integer example: 1434342123 custom_surcharge1: - description: "First custom surcharge amount" + description: 'First custom surcharge amount for the purchase order' type: number format: float example: 10.00 custom_surcharge2: - description: 'Second Custom Surcharge' + description: 'Second custom surcharge amount for the purchase order' type: number format: float - example: '10.00' + example: 10.00 custom_surcharge3: - description: 'Third Custom Surcharge' + description: 'Third custom surcharge amount for the purchase order' type: number format: float - example: '10.00' + example: 10.00 custom_surcharge4: - description: 'Fourth Custom Surcharge' + description: 'Fourth custom surcharge amount for the purchase order' type: number format: float - example: '10.00' + example: 10.00 custom_surcharge_tax1: - description: 'Toggles charging taxes on custom surcharge amounts' + description: 'Boolean flag indicating if taxes are charged on the first custom surcharge amount' type: boolean example: true custom_surcharge_tax2: - description: 'Toggles charging taxes on custom surcharge amounts' + description: 'Boolean flag indicating if taxes are charged on the second custom surcharge amount' type: boolean example: true custom_surcharge_tax3: - description: 'Toggles charging taxes on custom surcharge amounts' + description: 'Boolean flag indicating if taxes are charged on the third custom surcharge amount' type: boolean example: true custom_surcharge_tax4: - description: 'Toggles charging taxes on custom surcharge amounts' + description: 'Boolean flag indicating if taxes are charged on the fourth custom surcharge amount' type: boolean example: true type: object - Subscription: + + + ClientSettings: + required: + - currency_id properties: - id: - description: Unique identifier for the subscription - type: string - example: Opnel5aKBz - user_id: - description: Unique identifier for the user associated with the subscription - type: string - example: Ua6Rw4pVbS - product_id: - description: Unique identifier for the product associated with the subscription - type: string - example: Pr5Ft7yBmC - recurring_invoice_id: - description: Unique identifier for the recurring invoice associated with the subscription - type: string - example: Ri2Yt8zJkP - is_recurring: - description: Indicates whether the subscription is recurring - type: boolean - example: 'true' - frequency_id: - description: 'integer const representation of the frequency' - type: string - example: '1' - auto_bill: - description: 'enum setting' - type: string - example: always - promo_code: - description: Promotional code applied to the subscription - type: string - example: PROMOCODE4U - promo_discount: - description: Discount percentage or amount applied to the subscription - type: number - example: 10 - is_amount_discount: - description: Indicates whether the discount is a fixed amount - type: boolean - example: 'true' - allow_cancellation: - description: Indicates whether the subscription can be cancelled - type: boolean - example: 'true' - per_seat_enabled: - description: Indicates whether the subscription pricing is per seat - type: boolean - example: 'true' currency_id: - description: Unique identifier for the currency used in the subscription + description: 'The default currency id' + type: string + example: true + timezone_id: + description: 'The timezone id' + type: string + example: '15' + date_format_id: + description: 'The date format id' + type: string + example: '15' + military_time: + description: 'Toggles 12/24 hour time' + type: boolean + example: true + language_id: + description: 'The language id' + type: string + example: '1' + show_currency_code: + description: 'Toggles whether the currency symbol or code is shown' + type: boolean + example: true + payment_terms: + description: '-1 sets no payment term, 0 sets payment due immediately, positive integers indicates payment terms in days' type: integer example: '1' - max_seats_limit: - description: Maximum number of seats allowed for the subscription - type: integer - example: '100' - trial_enabled: - description: Indicates whether the subscription has a trial period - type: boolean - example: 'true' - trial_duration: - description: Duration of the trial period in days - type: integer - example: '14' - allow_query_overrides: - description: Indicates whether query overrides are allowed for the subscription - type: boolean - example: 'true' - allow_plan_changes: - description: Indicates whether plan changes are allowed for the subscription - type: boolean - example: 'true' - refund_period: - description: Number of days within which refunds can be requested - type: integer - example: '30' - webhook_configuration: - description: Webhook configuration for the subscription + company_gateway_ids: + description: 'A commad separate list of available gateways' type: string - example: 'expand reference for this' - is_deleted: - description: Indicates whether the subscription has been deleted + example: '1,2,3,4' + custom_value1: + description: 'A Custom Label' + type: string + example: 'Custom Label' + custom_value2: + description: 'A Custom Label' + type: string + example: 'Custom Label' + custom_value3: + description: 'A Custom Label' + type: string + example: 'Custom Label' + custom_value4: + description: 'A Custom Label' + type: string + example: 'Custom Label' + default_task_rate: + description: 'The default task rate' + type: number + format: float + example: '10.00' + send_reminders: + description: 'Toggles whether reminders are sent' type: boolean - example: 'false' - archived_at: - description: Timestamp + example: true + enable_client_portal_tasks: + description: 'Show/hide the tasks panel in the client portal' + type: boolean + example: true + email_style: + description: 'options include plain,light,dark,custom' + type: string + example: light + reply_to_email: + description: 'The reply to email address' + type: string + example: email@gmail.com + bcc_email: + description: 'A comma separate list of BCC emails' + type: string + example: 'email@gmail.com, contact@gmail.com' + pdf_email_attachment: + description: 'Toggles whether to attach PDF as attachment' + type: boolean + example: true + ubl_email_attachment: + description: 'Toggles whether to attach UBL as attachment' + type: boolean + example: true + email_style_custom: + description: 'The custom template' + type: string + example: '' + counter_number_applied: + description: 'enum when the invoice number counter is set, ie when_saved, when_sent, when_paid' + type: string + example: when_sent + quote_number_applied: + description: 'enum when the quote number counter is set, ie when_saved, when_sent' + type: string + example: when_sent + custom_message_dashboard: + description: 'A custom message which is displayed on the dashboard' + type: string + example: 'Please pay invoices immediately' + custom_message_unpaid_invoice: + description: 'A custom message which is displayed in the client portal when a client is viewing a unpaid invoice.' + type: string + example: 'Please pay invoices immediately' + custom_message_paid_invoice: + description: 'A custom message which is displayed in the client portal when a client is viewing a paid invoice.' + type: string + example: 'Thanks for paying this invoice!' + custom_message_unapproved_quote: + description: 'A custom message which is displayed in the client portal when a client is viewing a unapproved quote.' + type: string + example: 'Please approve quote' + lock_invoices: + description: 'Toggles whether invoices are locked once sent and cannot be modified further' + type: boolean + example: true + auto_archive_invoice: + description: 'Toggles whether a invoice is archived immediately following payment' + type: boolean + example: true + auto_archive_quote: + description: 'Toggles whether a quote is archived after being converted to a invoice' + type: boolean + example: true + auto_convert_quote: + description: 'Toggles whether a quote is converted to a invoice when approved' + type: boolean + example: true + inclusive_taxes: + description: 'Boolean flag determining whether inclusive or exclusive taxes are used' + type: boolean + example: true + task_number_pattern: + description: 'Allows customisation of the task number pattern' + type: string + example: '{$year}-{$counter}' + task_number_counter: + description: 'The incrementing counter for tasks' + type: integer + example: '1' + reminder_send_time: + description: 'Time from UTC +0 when the email will be sent to the client' + type: integer + example: '32400' + expense_number_pattern: + description: 'Allows customisation of the expense number pattern' + type: string + example: '{$year}-{$counter}' + expense_number_counter: + description: 'The incrementing counter for expenses' + type: integer + example: '1' + vendor_number_pattern: + description: 'Allows customisation of the vendor number pattern' + type: string + example: '{$year}-{$counter}' + vendor_number_counter: + description: 'The incrementing counter for vendors' + type: integer + example: '1' + ticket_number_pattern: + description: 'Allows customisation of the ticket number pattern' + type: string + example: '{$year}-{$counter}' + ticket_number_counter: + description: 'The incrementing counter for tickets' + type: integer + example: '1' + payment_number_pattern: + description: 'Allows customisation of the payment number pattern' + type: string + example: '{$year}-{$counter}' + payment_number_counter: + description: 'The incrementing counter for payments' + type: integer + example: '1' + invoice_number_pattern: + description: 'Allows customisation of the invoice number pattern' + type: string + example: '{$year}-{$counter}' + invoice_number_counter: + description: 'The incrementing counter for invoices' + type: integer + example: '1' + quote_number_pattern: + description: 'Allows customisation of the quote number pattern' + type: string + example: '{$year}-{$counter}' + quote_number_counter: + description: 'The incrementing counter for quotes' + type: integer + example: '1' + client_number_pattern: + description: 'Allows customisation of the client number pattern' + type: string + example: '{$year}-{$counter}' + client_number_counter: + description: 'The incrementing counter for clients' + type: integer + example: '1' + credit_number_pattern: + description: 'Allows customisation of the credit number pattern' + type: string + example: '{$year}-{$counter}' + credit_number_counter: + description: 'The incrementing counter for credits' + type: integer + example: '1' + recurring_invoice_number_prefix: + description: 'This string is prepended to the recurring invoice number' + type: string + example: R + reset_counter_frequency_id: + description: 'CONSTANT which is used to apply the frequency which the counters are reset' + type: integer + example: '1' + reset_counter_date: + description: 'The explicit date which is used to reset counters' + type: string + example: '2019-01-01' + counter_padding: + description: 'Pads the counter with leading zeros' + type: integer + example: '1' + shared_invoice_quote_counter: + description: 'Flags whether to share the counter for invoices and quotes' + type: boolean + example: true + update_products: + description: 'Determines if client fields are updated from third party APIs' + type: boolean + example: true + convert_products: + description: '' + type: boolean + example: true + fill_products: + description: 'Automatically fill products based on product_key' + type: boolean + example: true + invoice_terms: + description: 'The default invoice terms' + type: string + example: 'Invoice Terms are...' + quote_terms: + description: 'The default quote terms' + type: string + example: 'Quote Terms are...' + invoice_taxes: + description: 'Taxes can be applied to the invoice' type: number - format: integer - example: '1434342123' - created_at: - description: Timestamp + example: '1' + invoice_design_id: + description: 'The default design id (invoice, quote etc)' + type: string + example: '1' + quote_design_id: + description: 'The default design id (invoice, quote etc)' + type: string + example: '1' + invoice_footer: + description: 'The default invoice footer' + type: string + example: '1' + invoice_labels: + description: 'JSON string of invoice labels' + type: string + example: '1' + tax_rate1: + description: 'The tax rate (float)' type: number - format: integer - example: '134341234234' - updated_at: - description: Timestamp + example: '10' + tax_name1: + description: 'The tax name' + type: string + example: GST + tax_rate2: + description: 'The tax rate (float)' type: number - format: integer - example: '134341234234' + example: '10' + tax_name2: + description: 'The tax name' + type: string + example: GST + tax_rate3: + description: 'The tax rate (float)' + type: number + example: '10' + tax_name3: + description: 'The tax name' + type: string + example: GST + payment_type_id: + description: 'The default payment type id' + type: string + example: '1' + custom_fields: + description: 'JSON string of custom fields' + type: string + example: '{}' + email_footer: + description: 'The default email footer' + type: string + example: 'A default email footer' + email_sending_method: + description: 'The email driver to use to send email, options include default, gmail' + type: string + example: default + gmail_sending_user_id: + description: 'The hashed_id of the user account to send email from' + type: string + example: F76sd34D + email_subject_invoice: + description: '' + type: string + example: 'Your Invoice Subject' + email_subject_quote: + description: '' + type: string + example: 'Your Quote Subject' + email_subject_payment: + description: '' + type: string + example: 'Your Payment Subject' + email_template_invoice: + description: 'The full template for invoice emails' + type: string + example: '' + email_template_quote: + description: 'The full template for quote emails' + type: string + example: '' + email_template_payment: + description: 'The full template for payment emails' + type: string + example: '' + email_subject_reminder1: + description: 'Email subject for Reminder' + type: string + example: '' + email_subject_reminder2: + description: 'Email subject for Reminder' + type: string + example: '' + email_subject_reminder3: + description: 'Email subject for Reminder' + type: string + example: '' + email_subject_reminder_endless: + description: 'Email subject for endless reminders' + type: string + example: '' + email_template_reminder1: + description: 'The full template for Reminder 1' + type: string + example: '' + email_template_reminder2: + description: 'The full template for Reminder 2' + type: string + example: '' + email_template_reminder3: + description: 'The full template for Reminder 3' + type: string + example: '' + email_template_reminder_endless: + description: 'The full template for enless reminders' + type: string + example: '' + enable_portal_password: + description: 'Toggles whether a password is required to log into the client portal' + type: boolean + example: true + show_accept_invoice_terms: + description: 'Toggles whether the terms dialogue is shown to the client' + type: boolean + example: true + show_accept_quote_terms: + description: 'Toggles whether the terms dialogue is shown to the client' + type: boolean + example: true + require_invoice_signature: + description: 'Toggles whether a invoice signature is required' + type: boolean + example: true + require_quote_signature: + description: 'Toggles whether a quote signature is required' + type: boolean + example: true + name: + description: 'The company name' + type: string + example: 'Acme Co' + company_logo: + description: 'The company logo file' + type: object + example: logo.png + website: + description: 'The company website URL' + type: string + example: www.acme.com + address1: + description: 'The company address line 1' + type: string + example: 'Suite 888' + address2: + description: 'The company address line 2' + type: string + example: '5 Jimbo Way' + city: + description: 'The company city' + type: string + example: Sydney + state: + description: 'The company state' + type: string + example: Florisa + postal_code: + description: 'The company zip/postal code' + type: string + example: '90210' + phone: + description: 'The company phone' + type: string + example: 555-213-3948 + email: + description: 'The company email' + type: string + example: joe@acme.co + country_id: + description: 'The country ID' + type: string + example: '1' + vat_number: + description: 'The company VAT/TAX ID number' + type: string + example: '32 120 377 720' + page_size: + description: 'The default page size' + type: string + example: A4 + font_size: + description: 'The font size' + type: number + example: '9' + primary_font: + description: 'The primary font' + type: string + example: roboto + secondary_font: + description: 'The secondary font' + type: string + example: roboto + hide_paid_to_date: + description: 'Flags whether to hide the paid to date field' + type: boolean + example: false + embed_documents: + description: 'Toggled whether to embed documents in the PDF' + type: boolean + example: false + all_pages_header: + description: 'The header for the PDF' + type: boolean + example: false + all_pages_footer: + description: 'The footer for the PDF' + type: boolean + example: false + document_email_attachment: + description: 'Toggles whether to attach documents in the email' + type: boolean + example: false + enable_client_portal_password: + description: 'Toggles password protection of the client portal' + type: boolean + example: false + enable_email_markup: + description: 'Toggles the use of markdown in emails' + type: boolean + example: false + enable_client_portal_dashboard: + description: 'Toggles whether the client dashboard is shown in the client portal' + type: boolean + example: false + enable_client_portal: + description: 'Toggles whether the entire client portal is displayed to the client, or only the context' + type: boolean + example: false + email_template_statement: + description: 'The body of the email for statements' + type: string + example: 'template matter' + email_subject_statement: + description: 'The subject of the email for statements' + type: string + example: 'subject matter' + signature_on_pdf: + description: 'Toggles whether the signature (if available) is displayed on the PDF' + type: boolean + example: false + quote_footer: + description: 'The default quote footer' + type: string + example: 'the quote footer' + email_subject_custom1: + description: 'Custom reminder template subject' + type: string + example: 'Custom Subject 1' + email_subject_custom2: + description: 'Custom reminder template subject' + type: string + example: 'Custom Subject 2' + email_subject_custom3: + description: 'Custom reminder template subject' + type: string + example: 'Custom Subject 3' + email_template_custom1: + description: 'Custom reminder template body' + type: string + example: '' + email_template_custom2: + description: 'Custom reminder template body' + type: string + example: '' + email_template_custom3: + description: 'Custom reminder template body' + type: string + example: '' + enable_reminder1: + description: 'Toggles whether this reminder is enabled' + type: boolean + example: false + enable_reminder2: + description: 'Toggles whether this reminder is enabled' + type: boolean + example: false + enable_reminder3: + description: 'Toggles whether this reminder is enabled' + type: boolean + example: false + num_days_reminder1: + description: 'The Reminder interval' + type: number + example: '9' + num_days_reminder2: + description: 'The Reminder interval' + type: number + example: '9' + num_days_reminder3: + description: 'The Reminder interval' + type: number + example: '9' + schedule_reminder1: + description: '(enum: after_invoice_date, before_due_date, after_due_date)' + type: string + example: after_invoice_date + schedule_reminder2: + description: '(enum: after_invoice_date, before_due_date, after_due_date)' + type: string + example: after_invoice_date + schedule_reminder3: + description: '(enum: after_invoice_date, before_due_date, after_due_date)' + type: string + example: after_invoice_date + late_fee_amount1: + description: 'The late fee amount for reminder 1' + type: number + example: 10 + late_fee_amount2: + description: 'The late fee amount for reminder 2' + type: number + example: 20 + late_fee_amount3: + description: 'The late fee amount for reminder 2' + type: number + example: 100 + endless_reminder_frequency_id: + description: 'The frequency id of the endless reminder' + type: string + example: '1' + client_online_payment_notification: + description: 'Determines if a client should receive the notification for a online payment' + type: boolean + example: false + client_manual_payment_notification: + description: 'Determines if a client should receive the notification for a manually entered payment' + type: boolean + example: false + enable_e_invoice: + description: 'Determines if e-invoicing is enabled' + type: boolean + example: false + default_expense_payment_type_id: + description: 'The default payment type for expenses' + type: string + example: '0' + e_invoice_type: + description: 'The e-invoice type' + type: string + example: 'EN16931' + mailgun_endpoint: + description: 'The mailgun endpoint - used to determine whether US or EU endpoints are used' + type: string + example: 'api.mailgun.net or api.eu.mailgun.net' + client_initiated_payments: + description: 'Determines if clients can initiate payments directly from the client portal' + type: boolean + example: false + client_initiated_payments_minimum: + description: 'The minimum amount a client can pay' + type: number + example: 10 + sync_invoice_quote_columns: + description: 'Determines if invoice and quote columns are synced for the PDF rendering, or if they use their own columns' + type: boolean + example: false + show_task_item_description: + description: 'Determines if the task item description is shown on the invoice' + type: boolean + example: false + allow_billable_task_items: + description: 'Determines if task items can be marked as billable' + type: boolean + example: false + accept_client_input_quote_approval: + description: 'Determines if clients can approve quotes and also pass through a PO Number reference' + type: boolean + example: false + custom_sending_email: + description: 'When using Mailgun or Postmark, the FROM email address can be customized using this setting.' + type: string + example: 'bob@gmail.com' + show_paid_stamp: + description: 'Determines if the PAID stamp is shown on the invoice' + type: boolean + example: false + show_shipping_address: + description: 'Determines if the shipping address is shown on the invoice' + type: boolean + example: false + company_logo_size: + description: 'The size of the company logo on the PDF - percentage value between 0 and 100' + type: number + example: 100 + show_email_footer: + description: 'Determines if the email footer is shown on emails' + type: boolean + example: false + email_alignment: + description: 'The alignment of the email body text, options include left / center / right' + type: string + example: 'left' + auto_bill_standard_invoices: + description: 'Determines if standard invoices are automatically billed when they are created or due' + type: boolean + example: false + postmark_secret: + description: 'The Postmark secret API key' + type: string + example: '123456' + mailgun_secret: + description: 'The Mailgun secret API key' + type: string + example: '123456' + mailgun_domain: + description: 'The Mailgun domain' + type: string + example: 'sandbox123456.mailgun.org' + send_email_on_mark_paid: + description: 'Determines if an email is sent when an invoice is marked as paid' + type: boolean + example: false + vendor_portal_enable_uploads: + description: 'Determines if vendors can upload files to the portal' + type: boolean + example: false + besr_id: + description: 'The BESR ID' + type: string + example: '123456' + qr_iban: + description: 'The IBAN for the QR code' + type: string + example: 'CH123456' + email_subject_purchase_order: + description: 'The email subject for purchase orders' + type: string + example: 'Purchase Order' + email_template_purchase_order: + description: 'The email template for purchase orders' + type: string + example: 'Please see attached your purchase order.' + require_purchase_order_signature: + description: 'Determines if a signature is required on purchase orders' + type: boolean + example: false + purchase_order_public_notes: + description: 'The public notes for purchase orders' + type: string + example: 'Please see attached your purchase order.' + purchase_order_terms: + description: 'The terms for purchase orders' + type: string + example: 'Please see attached your purchase order.' + purchase_order_footer: + description: 'The footer for purchase orders' + type: string + example: 'Please see attached your purchase order.' + purchase_order_design_id: + description: 'The design id for purchase orders' + type: string + example: 'hd677df' + purchase_order_number_pattern: + description: 'The pattern for purchase order numbers' + type: string + example: 'PO-000000' + purchase_order_number_counter: + description: 'The counter for purchase order numbers' + type: number + example: 1 + page_numbering_alignment: + description: 'The alignment for page numbering: options include left / center / right' + type: string + example: 'left' + page_numbering: + description: 'Determines if page numbering is enabled on Document PDFs' + type: boolean + example: false + auto_archive_invoice_cancelled: + description: 'Determines if invoices are automatically archived when they are cancelled' + type: boolean + example: false + email_from_name: + description: 'The FROM name for emails when using Custom emailers' + type: string + example: 'Bob Smith' + show_all_tasks_client_portal: + description: 'Determines if all tasks are shown on the client portal' + type: boolean + example: false + entity_send_time: + description: 'The time that emails are sent. The time is localized to the clients locale, integer values from 1 - 24' + type: integer + example: 9 + shared_invoice_credit_counter: + description: 'Determines if the invoice and credit counter are shared' + type: boolean + example: false + reply_to_name: + description: 'The reply to name for emails' + type: string + example: 'Bob Smith' + hide_empty_columns_on_pdf: + description: 'Determines if empty columns are hidden on PDFs' + type: boolean + example: false + enable_reminder_endless: + description: 'Determines if endless reminders are enabled' + type: boolean + example: false + use_credits_payment: + description: 'Determines if credits can be used as a payment method' + type: boolean + example: false + recurring_invoice_number_pattern: + description: 'The pattern for recurring invoice numbers' + type: string + example: 'R-000000' + recurring_invoice_number_counter: + description: 'The counter for recurring invoice numbers' + type: number + example: 1 + client_portal_under_payment_minimum: + description: 'The minimum payment payment' + type: number + example: 10 + auto_bill_date: + description: 'Determines when the invoices are auto billed, options are on_send_date (when the invoice is sent) or on_due_date (when the invoice is due))' + type: string + example: 'on_send_date' + primary_color: + description: 'The primary color for the client portal / document highlights' + type: string + example: '#ffffff' + secondary_color: + description: 'The secondary color for the client portal / document highlights' + type: string + example: '#ffffff' + client_portal_allow_under_payment: + description: 'Determines if clients can pay invoices under the invoice amount due' + type: boolean + example: false + client_portal_allow_over_payment: + description: 'Determines if clients can pay invoices over the invoice amount' + type: boolean + example: false + auto_bill: + description: 'Determines how autobilling is applied for recurring invoices. off (no auto billed), always (always auto bill), optin (The user must opt in to auto billing), optout (The user must opt out of auto billing' + type: string + example: 'off' + client_portal_terms: + description: 'The terms which are displayed on the client portal' + type: string + example: 'Please see attached your invoice.' + client_portal_privacy_policy: + description: 'The privacy policy which is displayed on the client portal' + type: string + example: 'These are the terms of use for using the client portal.' + client_can_register: + description: 'Determines if clients can register on the client portal' + type: boolean + example: false + portal_design_id: + description: 'The design id for the client portal' + type: string + example: 'hd677df' + late_fee_endless_percent: + description: 'The late fee percentage for endless late fees' + type: number + example: 10 + late_fee_endless_amount: + description: 'The late fee amount for endless late fees' + type: number + example: 10 + auto_email_invoice: + description: 'Determines if invoices are automatically emailed when they are created' + type: boolean + example: false + email_signature: + description: 'The email signature for emails' + type: string + example: 'Bob Smith' type: object - BulkAction: - type: array - items: - type: integer - example: '[0,1,2,3,]' - Project: + Product: type: object properties: id: - description: 'The project hashed id' type: string - example: Opnel5aKBz + description: 'The hashed product ID.' + example: eP01N + readOnly: true user_id: - description: 'The user hashed id' type: string - example: Opnel5aKBz + description: 'The hashed ID of the user that created this product.' + example: n30m4 + readOnly: true assigned_user_id: - description: The assigned user identifier associated with the project type: string - example: Opnel5aKBz - client_id: + description: 'The hashed ID of the user assigned to this product.' + example: pR0j3 + project_id: type: string - example: Opnel5aKBz - description: The client identifier associated with the project - name: + description: 'The hashed ID of the project that this product is associated with.' + example: pR0j3 + vendor_id: type: string - description: The name of the project - example: 'New Project' - task_rate: - type: number - format: float - example: 10 - description: The default rate per task for the project - due_date: - type: string - format: date - example: '2019-01-01' - description: The due date for the project - private_notes: - type: string - description: Private notes associated with the project - budgeted_hours: - type: number - format: float - description: The number of budgeted hours for the project + description: 'The hashed ID of the vendor that this product is associated with.' + example: pR0j3 custom_value1: type: string - description: Custom value field 1 + description: 'Custom value field 1.' + example: 'Custom value 1' custom_value2: type: string - description: Custom value field 2 + description: 'Custom value field 2.' + example: 'Custom value 2' custom_value3: type: string - description: Custom value field 3 + description: 'Custom value field 3.' + example: 'Custom value 3' custom_value4: type: string - description: Custom value field 4 - created_at: - type: number - format: integer - example: 134341234234 - description: The timestamp of the project creation - updated_at: - type: number - format: integer - example: 134341234234 - description: The timestamp of the last project update - archived_at: - type: number - format: integer - example: 134341234234 - description: The timestamp of the project deletion - public_notes: + description: 'Custom value field 4.' + example: 'Custom value 4' + product_key: type: string - description: Public notes associated with the project + description: 'The product key.' + example: '1234' + notes: + type: string + description: 'Notes about the product.' + example: 'These are some notes about the product.' + cost: + type: number + format: double + description: 'The cost of the product. (Your purchase price for this product)' + example: 10.0 + price: + type: number + format: double + description: 'The price of the product that you are charging.' + example: 20.0 + quantity: + type: number + format: double + description: 'The quantity of the product. (used as a default)' + example: 5.0 + tax_name1: + type: string + description: 'The name of tax 1.' + example: 'Tax 1' + tax_rate1: + type: number + format: double + description: 'The rate of tax 1.' + example: 10.0 + tax_name2: + type: string + description: 'The name of tax 2.' + example: 'Tax 2' + tax_rate2: + type: number + format: double + description: 'The rate of tax 2.' + example: 5.0 + tax_name3: + type: string + description: 'The name of tax 3.' + example: 'Tax 3' + tax_rate3: + type: number + format: double + description: 'The rate of tax 3.' + example: 0.0 + archived_at: + type: integer + format: timestamp + description: 'The timestamp when the product was archived.' + example: '2022-03-18T15:00:00Z' + readOnly: true + created_at: + type: integer + format: timestamp + description: 'The timestamp when the product was created.' + example: '2022-03-18T15:00:00Z' + readOnly: true + updated_at: + description: Timestamp + type: integer + format: timestamp + example: '2022-03-18T12:34:56.789Z' + readOnly: true is_deleted: type: boolean - description: A flag indicating if the project is deleted - number: + description: 'Boolean flag determining if the product has been deleted' + example: false + readOnly: true + in_stock_quantity: + type: integer + format: int32 + description: The quantity of the product that is currently in stock + default: 0 + stock_notification: + type: boolean + description: Indicates whether stock notifications are enabled for this product + default: true + stock_notification_threshold: + type: integer + format: int32 + description: The minimum quantity threshold for which stock notifications will be triggered + default: 0 + max_quantity: + type: integer + format: int32 + description: The maximum quantity that can be ordered for this product + product_image: type: string - description: The project number - color: + description: The URL of the product image + format: uri-reference + tax_id: type: string - description: The color associated with the project - required: - - id - - user_id - - name - - task_rate - - budgeted_hours - - is_deleted - - color + default: '1' + description: | + The tax category id for this product.' + The following constants are available (default = '1') + + ``` + PRODUCT_TYPE_PHYSICAL = '1' + PRODUCT_TYPE_SERVICE = '2' + PRODUCT_TYPE_DIGITAL = '3' + PRODUCT_TYPE_SHIPPING = '4' + PRODUCT_TYPE_EXEMPT = '5' + PRODUCT_TYPE_REDUCED_TAX = '6' + PRODUCT_TYPE_OVERRIDE_TAX = '7' + PRODUCT_TYPE_ZERO_RATED = '8' + PRODUCT_TYPE_REVERSE_TAX = '9' + ``` + example: '1' + + ClientContactRequest: + properties: + id: + description: 'The hashed if of the contact' + type: string + example: Opnel5aKBz + readOnly: true + first_name: + description: 'The first name of the contact' + type: string + example: John + last_name: + description: 'The last name of the contact' + type: string + example: Doe + phone: + description: 'The phone number of the contact' + type: string + example: 555-152-4524 + custom_value1: + description: 'A Custom field value' + type: string + example: '' + custom_value2: + description: 'A Custom field value' + type: string + example: '' + custom_value3: + description: 'A Custom field value' + type: string + example: '' + custom_value4: + description: 'A Custom field value' + type: string + example: '' + email: + description: 'The email of the contact' + type: string + example: '' + password: + description: 'The hashed password of the contact' + type: string + example: '*****' + send_email: + description: 'Boolean value determines is this contact should receive emails' + type: boolean + example: true + type: object + ClientRequest: + required: + - contacts + - country_id + properties: + id: + description: 'The unique identifier of the client' + type: string + example: Opnel5aKBz + readOnly: true + contacts: + type: array + description: 'A list of contacts associated with the client' + items: + $ref: '#/components/schemas/ClientContactRequest' + name: + description: 'The name of the client company or organization' + type: string + example: "Jim's Housekeeping" + website: + description: 'The website URL of the client company or organization' + type: string + example: 'https://www.jims-housekeeping.com' + private_notes: + description: 'Notes that are only visible to the user who created the client' + type: string + example: 'Client prefers email communication over phone calls' + industry_id: + description: 'The unique identifier of the industry the client operates in' + type: number + example: '5' + size_id: + description: 'The unique identifier for the size category of the client company or organization' + type: number + example: '2' + address1: + description: "First line of the client's address" + type: string + example: '123 Main St' + address2: + description: "Second line of the client's address, if needed" + type: string + example: 'Apt 4B' + city: + description: 'The city the client is located in' + type: string + example: 'Beverly Hills' + state: + description: 'The state, province, or locality the client is located in' + type: string + example: 'California' + postal_code: + description: 'The postal code or ZIP code of the client' + type: string + example: '90210' + phone: + description: "The client's phone number" + type: string + example: '555-3434-3434' + country_id: + description: "The unique identifier of the client's country" + type: number + format: integer + example: '1' + custom_value1: + description: 'A custom field for storing additional information' + type: string + example: 'Preferred contact: Email' + custom_value2: + description: 'A custom field for storing additional information' + type: string + example: 'Account manager: John Doe' + custom_value3: + description: 'A custom field for storing additional information' + type: string + example: 'VIP client: Yes' + custom_value4: + description: 'A custom field for storing additional information' + type: string + example: 'Annual contract value: $50,000' + vat_number: + description: "The client's VAT (Value Added Tax) number, if applicable" + type: string + example: 'VAT123456' + id_number: + description: 'A unique identification number for the client, such as a tax ID or business registration number' + type: string + number: + description: 'A system-assigned unique number for the client, typically used for invoicing purposes' + type: string + example: 'CL-0001' + shipping_address1: + description: "First line of the client's shipping address" + type: string + example: '5 Wallaby Way' + shipping_address2: + description: "Second line of the client's shipping address, if needed" + type: string + example: 'Suite 5' + shipping_city: + description: "The city of the client's shipping address" + type: string + example: 'Perth' + shipping_state: + description: "The state, province, or locality of the client's shipping address" + type: string + example: 'Western Australia' + shipping_postal_code: + description: "The postal code or ZIP code of the client's shipping address" + type: string + example: '6110' + shipping_country_id: + description: "The unique identifier of the country for the client's shipping address" + type: number + format: integer + example: '4' + is_deleted: + description: 'A boolean value indicating whether the client has been deleted or not' + type: boolean + example: false + readOnly: true + group_settings_id: + description: 'The group settings assigned to the client' + type: string + example: Opnel5aKBz + routing_id: + description: 'The routing address id for e-invoicing for this client' + type: string + example: Opnel5aKBz3489-dfkiu-2239-sdsd + is_tax_exempt: + description: 'Flag which defines if the client is exempt from taxes' + type: boolean + example: false + has_valid_vat_number: + description: 'Flag which defines if the client has a valid VAT number' + type: boolean + example: false + readOnly: true + classification: + description: 'The classification of the client' + type: string + example: 'individual' + settings: + $ref: '#/components/schemas/ClientSettings' + type: object + Error: + properties: + message: + description: 'Something terrible went wrong' + type: string + example: 'Unexpected error' + code: + description: 'The HTTP error code, ie 5xx 4xx' + type: integer + example: '500' + type: object + BTRules: + properties: + data_key: + description: 'The key to search' + type: string + example: 'description,amount' + operator: + description: 'The operator flag of the search' + type: string + example: '>' + value: + description: 'The value to search for' + type: string + example: bob + type: object CompanySettings: required: - currency_id @@ -18704,2685 +21518,6 @@ components: example: "individual" type: object - Design: - properties: - id: - description: 'The design hashed id' - type: string - example: AS3df3A - name: - description: 'The design name' - type: string - example: Beauty - design: - description: 'The design HTML' - type: string - example: '' - is_custom: - description: 'Flag to determine if the design is a custom user design' - type: boolean - example: true - is_active: - description: 'Flag to determine if the design is available for use' - type: boolean - example: true - is_deleted: - description: 'Flag to determine if the design is deleted' - type: boolean - example: true - created_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - updated_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - deleted_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - type: object - ClientSettings: - required: - - currency_id - properties: - currency_id: - description: 'The default currency id' - type: string - example: true - timezone_id: - description: 'The timezone id' - type: string - example: '15' - date_format_id: - description: 'The date format id' - type: string - example: '15' - military_time: - description: 'Toggles 12/24 hour time' - type: boolean - example: true - language_id: - description: 'The language id' - type: string - example: '1' - show_currency_code: - description: 'Toggles whether the currency symbol or code is shown' - type: boolean - example: true - payment_terms: - description: '-1 sets no payment term, 0 sets payment due immediately, positive integers indicates payment terms in days' - type: integer - example: '1' - company_gateway_ids: - description: 'A commad separate list of available gateways' - type: string - example: '1,2,3,4' - custom_value1: - description: 'A Custom Label' - type: string - example: 'Custom Label' - custom_value2: - description: 'A Custom Label' - type: string - example: 'Custom Label' - custom_value3: - description: 'A Custom Label' - type: string - example: 'Custom Label' - custom_value4: - description: 'A Custom Label' - type: string - example: 'Custom Label' - default_task_rate: - description: 'The default task rate' - type: number - format: float - example: '10.00' - send_reminders: - description: 'Toggles whether reminders are sent' - type: boolean - example: true - enable_client_portal_tasks: - description: 'Show/hide the tasks panel in the client portal' - type: boolean - example: true - email_style: - description: 'options include plain,light,dark,custom' - type: string - example: light - reply_to_email: - description: 'The reply to email address' - type: string - example: email@gmail.com - bcc_email: - description: 'A comma separate list of BCC emails' - type: string - example: 'email@gmail.com, contact@gmail.com' - pdf_email_attachment: - description: 'Toggles whether to attach PDF as attachment' - type: boolean - example: true - ubl_email_attachment: - description: 'Toggles whether to attach UBL as attachment' - type: boolean - example: true - email_style_custom: - description: 'The custom template' - type: string - example: '' - counter_number_applied: - description: 'enum when the invoice number counter is set, ie when_saved, when_sent, when_paid' - type: string - example: when_sent - quote_number_applied: - description: 'enum when the quote number counter is set, ie when_saved, when_sent' - type: string - example: when_sent - custom_message_dashboard: - description: 'A custom message which is displayed on the dashboard' - type: string - example: 'Please pay invoices immediately' - custom_message_unpaid_invoice: - description: 'A custom message which is displayed in the client portal when a client is viewing a unpaid invoice.' - type: string - example: 'Please pay invoices immediately' - custom_message_paid_invoice: - description: 'A custom message which is displayed in the client portal when a client is viewing a paid invoice.' - type: string - example: 'Thanks for paying this invoice!' - custom_message_unapproved_quote: - description: 'A custom message which is displayed in the client portal when a client is viewing a unapproved quote.' - type: string - example: 'Please approve quote' - lock_invoices: - description: 'Toggles whether invoices are locked once sent and cannot be modified further' - type: boolean - example: true - auto_archive_invoice: - description: 'Toggles whether a invoice is archived immediately following payment' - type: boolean - example: true - auto_archive_quote: - description: 'Toggles whether a quote is archived after being converted to a invoice' - type: boolean - example: true - auto_convert_quote: - description: 'Toggles whether a quote is converted to a invoice when approved' - type: boolean - example: true - inclusive_taxes: - description: 'Boolean flag determining whether inclusive or exclusive taxes are used' - type: boolean - example: true - task_number_pattern: - description: 'Allows customisation of the task number pattern' - type: string - example: '{$year}-{$counter}' - task_number_counter: - description: 'The incrementing counter for tasks' - type: integer - example: '1' - reminder_send_time: - description: 'Time from UTC +0 when the email will be sent to the client' - type: integer - example: '32400' - expense_number_pattern: - description: 'Allows customisation of the expense number pattern' - type: string - example: '{$year}-{$counter}' - expense_number_counter: - description: 'The incrementing counter for expenses' - type: integer - example: '1' - vendor_number_pattern: - description: 'Allows customisation of the vendor number pattern' - type: string - example: '{$year}-{$counter}' - vendor_number_counter: - description: 'The incrementing counter for vendors' - type: integer - example: '1' - ticket_number_pattern: - description: 'Allows customisation of the ticket number pattern' - type: string - example: '{$year}-{$counter}' - ticket_number_counter: - description: 'The incrementing counter for tickets' - type: integer - example: '1' - payment_number_pattern: - description: 'Allows customisation of the payment number pattern' - type: string - example: '{$year}-{$counter}' - payment_number_counter: - description: 'The incrementing counter for payments' - type: integer - example: '1' - invoice_number_pattern: - description: 'Allows customisation of the invoice number pattern' - type: string - example: '{$year}-{$counter}' - invoice_number_counter: - description: 'The incrementing counter for invoices' - type: integer - example: '1' - quote_number_pattern: - description: 'Allows customisation of the quote number pattern' - type: string - example: '{$year}-{$counter}' - quote_number_counter: - description: 'The incrementing counter for quotes' - type: integer - example: '1' - client_number_pattern: - description: 'Allows customisation of the client number pattern' - type: string - example: '{$year}-{$counter}' - client_number_counter: - description: 'The incrementing counter for clients' - type: integer - example: '1' - credit_number_pattern: - description: 'Allows customisation of the credit number pattern' - type: string - example: '{$year}-{$counter}' - credit_number_counter: - description: 'The incrementing counter for credits' - type: integer - example: '1' - recurring_invoice_number_prefix: - description: 'This string is prepended to the recurring invoice number' - type: string - example: R - reset_counter_frequency_id: - description: 'CONSTANT which is used to apply the frequency which the counters are reset' - type: integer - example: '1' - reset_counter_date: - description: 'The explicit date which is used to reset counters' - type: string - example: '2019-01-01' - counter_padding: - description: 'Pads the counter with leading zeros' - type: integer - example: '1' - shared_invoice_quote_counter: - description: 'Flags whether to share the counter for invoices and quotes' - type: boolean - example: true - update_products: - description: 'Determines if client fields are updated from third party APIs' - type: boolean - example: true - convert_products: - description: '' - type: boolean - example: true - fill_products: - description: 'Automatically fill products based on product_key' - type: boolean - example: true - invoice_terms: - description: 'The default invoice terms' - type: string - example: 'Invoice Terms are...' - quote_terms: - description: 'The default quote terms' - type: string - example: 'Quote Terms are...' - invoice_taxes: - description: 'Taxes can be applied to the invoice' - type: number - example: '1' - invoice_design_id: - description: 'The default design id (invoice, quote etc)' - type: string - example: '1' - quote_design_id: - description: 'The default design id (invoice, quote etc)' - type: string - example: '1' - invoice_footer: - description: 'The default invoice footer' - type: string - example: '1' - invoice_labels: - description: 'JSON string of invoice labels' - type: string - example: '1' - tax_rate1: - description: 'The tax rate (float)' - type: number - example: '10' - tax_name1: - description: 'The tax name' - type: string - example: GST - tax_rate2: - description: 'The tax rate (float)' - type: number - example: '10' - tax_name2: - description: 'The tax name' - type: string - example: GST - tax_rate3: - description: 'The tax rate (float)' - type: number - example: '10' - tax_name3: - description: 'The tax name' - type: string - example: GST - payment_type_id: - description: 'The default payment type id' - type: string - example: '1' - custom_fields: - description: 'JSON string of custom fields' - type: string - example: '{}' - email_footer: - description: 'The default email footer' - type: string - example: 'A default email footer' - email_sending_method: - description: 'The email driver to use to send email, options include default, gmail' - type: string - example: default - gmail_sending_user_id: - description: 'The hashed_id of the user account to send email from' - type: string - example: F76sd34D - email_subject_invoice: - description: '' - type: string - example: 'Your Invoice Subject' - email_subject_quote: - description: '' - type: string - example: 'Your Quote Subject' - email_subject_payment: - description: '' - type: string - example: 'Your Payment Subject' - email_template_invoice: - description: 'The full template for invoice emails' - type: string - example: '' - email_template_quote: - description: 'The full template for quote emails' - type: string - example: '' - email_template_payment: - description: 'The full template for payment emails' - type: string - example: '' - email_subject_reminder1: - description: 'Email subject for Reminder' - type: string - example: '' - email_subject_reminder2: - description: 'Email subject for Reminder' - type: string - example: '' - email_subject_reminder3: - description: 'Email subject for Reminder' - type: string - example: '' - email_subject_reminder_endless: - description: 'Email subject for endless reminders' - type: string - example: '' - email_template_reminder1: - description: 'The full template for Reminder 1' - type: string - example: '' - email_template_reminder2: - description: 'The full template for Reminder 2' - type: string - example: '' - email_template_reminder3: - description: 'The full template for Reminder 3' - type: string - example: '' - email_template_reminder_endless: - description: 'The full template for enless reminders' - type: string - example: '' - enable_portal_password: - description: 'Toggles whether a password is required to log into the client portal' - type: boolean - example: true - show_accept_invoice_terms: - description: 'Toggles whether the terms dialogue is shown to the client' - type: boolean - example: true - show_accept_quote_terms: - description: 'Toggles whether the terms dialogue is shown to the client' - type: boolean - example: true - require_invoice_signature: - description: 'Toggles whether a invoice signature is required' - type: boolean - example: true - require_quote_signature: - description: 'Toggles whether a quote signature is required' - type: boolean - example: true - name: - description: 'The company name' - type: string - example: 'Acme Co' - company_logo: - description: 'The company logo file' - type: object - example: logo.png - website: - description: 'The company website URL' - type: string - example: www.acme.com - address1: - description: 'The company address line 1' - type: string - example: 'Suite 888' - address2: - description: 'The company address line 2' - type: string - example: '5 Jimbo Way' - city: - description: 'The company city' - type: string - example: Sydney - state: - description: 'The company state' - type: string - example: Florisa - postal_code: - description: 'The company zip/postal code' - type: string - example: '90210' - phone: - description: 'The company phone' - type: string - example: 555-213-3948 - email: - description: 'The company email' - type: string - example: joe@acme.co - country_id: - description: 'The country ID' - type: string - example: '1' - vat_number: - description: 'The company VAT/TAX ID number' - type: string - example: '32 120 377 720' - page_size: - description: 'The default page size' - type: string - example: A4 - font_size: - description: 'The font size' - type: number - example: '9' - primary_font: - description: 'The primary font' - type: string - example: roboto - secondary_font: - description: 'The secondary font' - type: string - example: roboto - hide_paid_to_date: - description: 'Flags whether to hide the paid to date field' - type: boolean - example: false - embed_documents: - description: 'Toggled whether to embed documents in the PDF' - type: boolean - example: false - all_pages_header: - description: 'The header for the PDF' - type: boolean - example: false - all_pages_footer: - description: 'The footer for the PDF' - type: boolean - example: false - document_email_attachment: - description: 'Toggles whether to attach documents in the email' - type: boolean - example: false - enable_client_portal_password: - description: 'Toggles password protection of the client portal' - type: boolean - example: false - enable_email_markup: - description: 'Toggles the use of markdown in emails' - type: boolean - example: false - enable_client_portal_dashboard: - description: 'Toggles whether the client dashboard is shown in the client portal' - type: boolean - example: false - enable_client_portal: - description: 'Toggles whether the entire client portal is displayed to the client, or only the context' - type: boolean - example: false - email_template_statement: - description: 'The body of the email for statements' - type: string - example: 'template matter' - email_subject_statement: - description: 'The subject of the email for statements' - type: string - example: 'subject matter' - signature_on_pdf: - description: 'Toggles whether the signature (if available) is displayed on the PDF' - type: boolean - example: false - quote_footer: - description: 'The default quote footer' - type: string - example: 'the quote footer' - email_subject_custom1: - description: 'Custom reminder template subject' - type: string - example: 'Custom Subject 1' - email_subject_custom2: - description: 'Custom reminder template subject' - type: string - example: 'Custom Subject 2' - email_subject_custom3: - description: 'Custom reminder template subject' - type: string - example: 'Custom Subject 3' - email_template_custom1: - description: 'Custom reminder template body' - type: string - example: '' - email_template_custom2: - description: 'Custom reminder template body' - type: string - example: '' - email_template_custom3: - description: 'Custom reminder template body' - type: string - example: '' - enable_reminder1: - description: 'Toggles whether this reminder is enabled' - type: boolean - example: false - enable_reminder2: - description: 'Toggles whether this reminder is enabled' - type: boolean - example: false - enable_reminder3: - description: 'Toggles whether this reminder is enabled' - type: boolean - example: false - num_days_reminder1: - description: 'The Reminder interval' - type: number - example: '9' - num_days_reminder2: - description: 'The Reminder interval' - type: number - example: '9' - num_days_reminder3: - description: 'The Reminder interval' - type: number - example: '9' - schedule_reminder1: - description: '(enum: after_invoice_date, before_due_date, after_due_date)' - type: string - example: after_invoice_date - schedule_reminder2: - description: '(enum: after_invoice_date, before_due_date, after_due_date)' - type: string - example: after_invoice_date - schedule_reminder3: - description: '(enum: after_invoice_date, before_due_date, after_due_date)' - type: string - example: after_invoice_date - late_fee_amount1: - description: 'The late fee amount for reminder 1' - type: number - example: 10 - late_fee_amount2: - description: 'The late fee amount for reminder 2' - type: number - example: 20 - late_fee_amount3: - description: 'The late fee amount for reminder 2' - type: number - example: 100 - endless_reminder_frequency_id: - description: 'The frequency id of the endless reminder' - type: string - example: '1' - client_online_payment_notification: - description: 'Determines if a client should receive the notification for a online payment' - type: boolean - example: false - client_manual_payment_notification: - description: 'Determines if a client should receive the notification for a manually entered payment' - type: boolean - example: false - enable_e_invoice: - description: 'Determines if e-invoicing is enabled' - type: boolean - example: false - default_expense_payment_type_id: - description: 'The default payment type for expenses' - type: string - example: '0' - e_invoice_type: - description: 'The e-invoice type' - type: string - example: 'EN16931' - mailgun_endpoint: - description: 'The mailgun endpoint - used to determine whether US or EU endpoints are used' - type: string - example: 'api.mailgun.net or api.eu.mailgun.net' - client_initiated_payments: - description: 'Determines if clients can initiate payments directly from the client portal' - type: boolean - example: false - client_initiated_payments_minimum: - description: 'The minimum amount a client can pay' - type: number - example: 10 - sync_invoice_quote_columns: - description: 'Determines if invoice and quote columns are synced for the PDF rendering, or if they use their own columns' - type: boolean - example: false - show_task_item_description: - description: 'Determines if the task item description is shown on the invoice' - type: boolean - example: false - allow_billable_task_items: - description: 'Determines if task items can be marked as billable' - type: boolean - example: false - accept_client_input_quote_approval: - description: 'Determines if clients can approve quotes and also pass through a PO Number reference' - type: boolean - example: false - custom_sending_email: - description: 'When using Mailgun or Postmark, the FROM email address can be customized using this setting.' - type: string - example: 'bob@gmail.com' - show_paid_stamp: - description: 'Determines if the PAID stamp is shown on the invoice' - type: boolean - example: false - show_shipping_address: - description: 'Determines if the shipping address is shown on the invoice' - type: boolean - example: false - company_logo_size: - description: 'The size of the company logo on the PDF - percentage value between 0 and 100' - type: number - example: 100 - show_email_footer: - description: 'Determines if the email footer is shown on emails' - type: boolean - example: false - email_alignment: - description: 'The alignment of the email body text, options include left / center / right' - type: string - example: 'left' - auto_bill_standard_invoices: - description: 'Determines if standard invoices are automatically billed when they are created or due' - type: boolean - example: false - postmark_secret: - description: 'The Postmark secret API key' - type: string - example: '123456' - mailgun_secret: - description: 'The Mailgun secret API key' - type: string - example: '123456' - mailgun_domain: - description: 'The Mailgun domain' - type: string - example: 'sandbox123456.mailgun.org' - send_email_on_mark_paid: - description: 'Determines if an email is sent when an invoice is marked as paid' - type: boolean - example: false - vendor_portal_enable_uploads: - description: 'Determines if vendors can upload files to the portal' - type: boolean - example: false - besr_id: - description: 'The BESR ID' - type: string - example: '123456' - qr_iban: - description: 'The IBAN for the QR code' - type: string - example: 'CH123456' - email_subject_purchase_order: - description: 'The email subject for purchase orders' - type: string - example: 'Purchase Order' - email_template_purchase_order: - description: 'The email template for purchase orders' - type: string - example: 'Please see attached your purchase order.' - require_purchase_order_signature: - description: 'Determines if a signature is required on purchase orders' - type: boolean - example: false - purchase_order_public_notes: - description: 'The public notes for purchase orders' - type: string - example: 'Please see attached your purchase order.' - purchase_order_terms: - description: 'The terms for purchase orders' - type: string - example: 'Please see attached your purchase order.' - purchase_order_footer: - description: 'The footer for purchase orders' - type: string - example: 'Please see attached your purchase order.' - purchase_order_design_id: - description: 'The design id for purchase orders' - type: string - example: 'hd677df' - purchase_order_number_pattern: - description: 'The pattern for purchase order numbers' - type: string - example: 'PO-000000' - purchase_order_number_counter: - description: 'The counter for purchase order numbers' - type: number - example: 1 - page_numbering_alignment: - description: 'The alignment for page numbering: options include left / center / right' - type: string - example: 'left' - page_numbering: - description: 'Determines if page numbering is enabled on Document PDFs' - type: boolean - example: false - auto_archive_invoice_cancelled: - description: 'Determines if invoices are automatically archived when they are cancelled' - type: boolean - example: false - email_from_name: - description: 'The FROM name for emails when using Custom emailers' - type: string - example: 'Bob Smith' - show_all_tasks_client_portal: - description: 'Determines if all tasks are shown on the client portal' - type: boolean - example: false - entity_send_time: - description: 'The time that emails are sent. The time is localized to the clients locale, integer values from 1 - 24' - type: integer - example: 9 - shared_invoice_credit_counter: - description: 'Determines if the invoice and credit counter are shared' - type: boolean - example: false - reply_to_name: - description: 'The reply to name for emails' - type: string - example: 'Bob Smith' - hide_empty_columns_on_pdf: - description: 'Determines if empty columns are hidden on PDFs' - type: boolean - example: false - enable_reminder_endless: - description: 'Determines if endless reminders are enabled' - type: boolean - example: false - use_credits_payment: - description: 'Determines if credits can be used as a payment method' - type: boolean - example: false - recurring_invoice_number_pattern: - description: 'The pattern for recurring invoice numbers' - type: string - example: 'R-000000' - recurring_invoice_number_counter: - description: 'The counter for recurring invoice numbers' - type: number - example: 1 - client_portal_under_payment_minimum: - description: 'The minimum payment payment' - type: number - example: 10 - auto_bill_date: - description: 'Determines when the invoices are auto billed, options are on_send_date (when the invoice is sent) or on_due_date (when the invoice is due))' - type: string - example: 'on_send_date' - primary_color: - description: 'The primary color for the client portal / document highlights' - type: string - example: '#ffffff' - secondary_color: - description: 'The secondary color for the client portal / document highlights' - type: string - example: '#ffffff' - client_portal_allow_under_payment: - description: 'Determines if clients can pay invoices under the invoice amount due' - type: boolean - example: false - client_portal_allow_over_payment: - description: 'Determines if clients can pay invoices over the invoice amount' - type: boolean - example: false - auto_bill: - description: 'Determines how autobilling is applied for recurring invoices. off (no auto billed), always (always auto bill), optin (The user must opt in to auto billing), optout (The user must opt out of auto billing' - type: string - example: 'off' - client_portal_terms: - description: 'The terms which are displayed on the client portal' - type: string - example: 'Please see attached your invoice.' - client_portal_privacy_policy: - description: 'The privacy policy which is displayed on the client portal' - type: string - example: 'These are the terms of use for using the client portal.' - client_can_register: - description: 'Determines if clients can register on the client portal' - type: boolean - example: false - portal_design_id: - description: 'The design id for the client portal' - type: string - example: 'hd677df' - late_fee_endless_percent: - description: 'The late fee percentage for endless late fees' - type: number - example: 10 - late_fee_endless_amount: - description: 'The late fee amount for endless late fees' - type: number - example: 10 - auto_email_invoice: - description: 'Determines if invoices are automatically emailed when they are created' - type: boolean - example: false - email_signature: - description: 'The email signature for emails' - type: string - example: 'Bob Smith' - type: object - BTRules: - properties: - data_key: - description: 'The key to search' - type: string - example: 'description,amount' - operator: - description: 'The operator flag of the search' - type: string - example: '>' - value: - description: 'The value to search for' - type: string - example: bob - type: object - Account: - properties: - id: - description: 'The account hashed id' - type: string - example: AS3df3A - account_sms_verified: - description: 'Boolean flag if the account has been verified by sms' - type: string - example: true - type: object - Invoice: - properties: - id: - description: 'The invoice hashed id' - type: string - example: Opnel5aKBz - readOnly: true - user_id: - description: 'The user hashed id' - type: string - example: Opnel5aKBz - readOnly: true - assigned_user_id: - description: 'The assigned user hashed id' - type: string - example: Opnel5aKBz - client_id: - description: 'The client hashed id' - type: string - example: Opnel5aKBz - status_id: - description: 'The invoice status variable' - type: string - example: '4' - number: - description: 'The invoice number - is a unique alpha numeric number per invoice per company' - type: string - example: INV_101 - po_number: - description: 'The purchase order associated with this invoice' - type: string - example: PO-1234 - terms: - description: 'The invoice terms' - type: string - example: 'These are invoice terms' - public_notes: - description: 'The public notes of the invoice' - type: string - example: 'These are some public notes' - private_notes: - description: 'The private notes of the invoice' - type: string - example: 'These are some private notes' - footer: - description: 'The invoice footer notes' - type: string - example: '' - custom_value1: - description: 'A custom field value' - type: string - example: '2022-10-01' - custom_value2: - description: 'A custom field value' - type: string - example: 'Something custom' - custom_value3: - description: 'A custom field value' - type: string - example: '' - custom_value4: - description: 'A custom field value' - type: string - example: '' - tax_name1: - description: 'The tax name' - type: string - example: '' - tax_name2: - description: 'The tax name' - type: string - example: '' - tax_rate1: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_rate2: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_name3: - description: 'The tax name' - type: string - example: '' - tax_rate3: - description: 'The tax rate' - type: number - format: float - example: '10.00' - total_taxes: - description: 'The total taxes for the invoice' - type: number - format: float - example: '10.00' - line_items: - type: array - description: 'An array of objects which define the line items of the invoice' - items: - $ref: '#/components/schemas/InvoiceItem' - invitations: - type: array - description: 'An array of objects which define the invitations of the invoice' - items: - $ref: '#/components/schemas/InvoiceInvitation' - amount: - description: 'The invoice amount' - type: number - format: float - example: '10.00' - balance: - description: 'The invoice balance' - type: number - format: float - example: '10.00' - paid_to_date: - description: 'The amount paid on the invoice to date' - type: number - format: float - example: '10.00' - discount: - description: 'The invoice discount, can be an amount or a percentage' - type: number - format: float - example: '10.00' - partial: - description: 'The deposit/partial amount' - type: number - format: float - example: '10.00' - is_amount_discount: - description: 'Flag determining if the discount is an amount or a percentage' - type: boolean - example: true - is_deleted: - description: 'Defines if the invoice has been deleted' - type: boolean - example: true - uses_inclusive_taxes: - description: 'Defines the type of taxes used as either inclusive or exclusive' - type: boolean - example: true - date: - description: 'The Invoice Date' - type: string - format: date - example: '1994-07-30' - last_sent_date: - description: 'The last date the invoice was sent out' - type: string - format: date - example: '1994-07-30' - next_send_date: - description: 'The Next date for a reminder to be sent' - type: string - format: date - example: '1994-07-30' - partial_due_date: - description: 'The due date for the deposit/partial amount' - type: string - format: date - example: '1994-07-30' - due_date: - description: 'The due date of the invoice' - type: string - format: date - example: '1994-07-30' - last_viewed: - description: Timestamp - type: number - format: integer - example: '1434342123' - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - archived_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - custom_surcharge1: - description: 'First Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge2: - description: 'Second Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge3: - description: 'Third Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge4: - description: 'Fourth Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge_tax1: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - custom_surcharge_tax2: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - custom_surcharge_tax3: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - custom_surcharge_tax4: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - project_id: - description: 'The project associated with this invoice' - type: string - example: Opnel5aKBz - auto_bill_tries: - description: 'The number of times the invoice has attempted to be auto billed' - type: integer - example: '1' - readOnly: true - auto_bill_enabled: - description: 'Boolean flag determining if the invoice is set to auto bill' - type: boolean - example: true - subscription_id: - description: 'The subscription associated with this invoice' - type: string - example: Opnel5aKBz - - type: object - ClientRequest: - required: - - contacts - - country_id - properties: - id: - description: 'The unique identifier of the client' - type: string - example: Opnel5aKBz - readOnly: true - contacts: - type: array - description: 'A list of contacts associated with the client' - items: - $ref: '#/components/schemas/ClientContactRequest' - name: - description: 'The name of the client company or organization' - type: string - example: "Jim's Housekeeping" - website: - description: 'The website URL of the client company or organization' - type: string - example: 'https://www.jims-housekeeping.com' - private_notes: - description: 'Notes that are only visible to the user who created the client' - type: string - example: 'Client prefers email communication over phone calls' - industry_id: - description: 'The unique identifier of the industry the client operates in' - type: number - example: '5' - size_id: - description: 'The unique identifier for the size category of the client company or organization' - type: number - example: '2' - address1: - description: "First line of the client's address" - type: string - example: '123 Main St' - address2: - description: "Second line of the client's address, if needed" - type: string - example: 'Apt 4B' - city: - description: 'The city the client is located in' - type: string - example: 'Beverly Hills' - state: - description: 'The state, province, or locality the client is located in' - type: string - example: 'California' - postal_code: - description: 'The postal code or ZIP code of the client' - type: string - example: '90210' - phone: - description: "The client's phone number" - type: string - example: '555-3434-3434' - country_id: - description: "The unique identifier of the client's country" - type: number - format: integer - example: '1' - custom_value1: - description: 'A custom field for storing additional information' - type: string - example: 'Preferred contact: Email' - custom_value2: - description: 'A custom field for storing additional information' - type: string - example: 'Account manager: John Doe' - custom_value3: - description: 'A custom field for storing additional information' - type: string - example: 'VIP client: Yes' - custom_value4: - description: 'A custom field for storing additional information' - type: string - example: 'Annual contract value: $50,000' - vat_number: - description: "The client's VAT (Value Added Tax) number, if applicable" - type: string - example: 'VAT123456' - id_number: - description: 'A unique identification number for the client, such as a tax ID or business registration number' - type: string - number: - description: 'A system-assigned unique number for the client, typically used for invoicing purposes' - type: string - example: 'CL-0001' - shipping_address1: - description: "First line of the client's shipping address" - type: string - example: '5 Wallaby Way' - shipping_address2: - description: "Second line of the client's shipping address, if needed" - type: string - example: 'Suite 5' - shipping_city: - description: "The city of the client's shipping address" - type: string - example: 'Perth' - shipping_state: - description: "The state, province, or locality of the client's shipping address" - type: string - example: 'Western Australia' - shipping_postal_code: - description: "The postal code or ZIP code of the client's shipping address" - type: string - example: '6110' - shipping_country_id: - description: "The unique identifier of the country for the client's shipping address" - type: number - format: integer - example: '4' - is_deleted: - description: 'A boolean value indicating whether the client has been deleted or not' - type: boolean - example: false - readOnly: true - group_settings_id: - description: 'The group settings assigned to the client' - type: string - example: Opnel5aKBz - routing_id: - description: 'The routing address id for e-invoicing for this client' - type: string - example: Opnel5aKBz3489-dfkiu-2239-sdsd - is_tax_exempt: - description: 'Flag which defines if the client is exempt from taxes' - type: boolean - example: false - has_valid_vat_number: - description: 'Flag which defines if the client has a valid VAT number' - type: boolean - example: false - readOnly: true - classification: - description: 'The classification of the client' - type: string - example: 'individual' - settings: - $ref: '#/components/schemas/ClientSettings' - type: object - PaymentTerm: - properties: - num_days: - description: 'The payment term length in days' - type: integer - example: '1' - name: - description: 'The payment term length in string format' - type: string - example: 'NET 1' - created_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - updated_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - archived_at: - description: Timestamp - type: number - format: integer - example: '134341234234' - type: object - ClientContactRequest: - properties: - id: - description: 'The hashed if of the contact' - type: string - example: Opnel5aKBz - readOnly: true - first_name: - description: 'The first name of the contact' - type: string - example: John - last_name: - description: 'The last name of the contact' - type: string - example: Doe - phone: - description: 'The phone number of the contact' - type: string - example: 555-152-4524 - custom_value1: - description: 'A Custom field value' - type: string - example: '' - custom_value2: - description: 'A Custom field value' - type: string - example: '' - custom_value3: - description: 'A Custom field value' - type: string - example: '' - custom_value4: - description: 'A Custom field value' - type: string - example: '' - email: - description: 'The email of the contact' - type: string - example: '' - password: - description: 'The hashed password of the contact' - type: string - example: '*****' - send_email: - description: 'Boolean value determines is this contact should receive emails' - type: boolean - example: true - type: object - - GenericBulkAction: - properties: - action: - type: string - example: archive - description: 'The action to perform ie. archive / restore / delete' - ids: - type: array - items: - format: string - type: string - example: 2J234DFA,D2J234DFA,D2J234DFA - description: string array of client hashed ids - type: object - BankTransactionRule: - properties: - id: - description: 'The bank transaction rules hashed id' - type: string - example: AS3df3A - user_id: - description: 'The user hashed id' - type: string - example: AS3df3A - name: - description: 'The name of the transaction' - type: string - example: 'Rule 1' - rules: - description: 'A mapped collection of the sub rules for the BankTransactionRule' - type: array - items: - $ref: '#/components/schemas/BTRules' - auto_convert: - description: 'Flags whether the rule converts the transaction automatically' - type: boolean - example: true - matches_on_all: - description: 'Flags whether all subrules are required for the match' - type: boolean - example: true - applies_to: - description: 'Flags whether the rule applies to a CREDIT or DEBIT' - type: string - example: CREDIT - client_id: - description: 'The client hashed id' - type: string - example: AS3df3A - vendor_id: - description: 'The vendor hashed id' - type: string - example: AS3df3A - category_id: - description: 'The category hashed id' - type: string - example: AS3df3A - type: object - Product: - type: object - properties: - id: - type: string - description: 'The hashed product ID.' - example: eP01N - readOnly: true - user_id: - type: string - description: 'The hashed ID of the user that created this product.' - example: n30m4 - readOnly: true - assigned_user_id: - type: string - description: 'The hashed ID of the user assigned to this product.' - example: pR0j3 - project_id: - type: string - description: 'The hashed ID of the project that this product is associated with.' - example: pR0j3 - vendor_id: - type: string - description: 'The hashed ID of the vendor that this product is associated with.' - example: pR0j3 - custom_value1: - type: string - description: 'Custom value field 1.' - example: 'Custom value 1' - custom_value2: - type: string - description: 'Custom value field 2.' - example: 'Custom value 2' - custom_value3: - type: string - description: 'Custom value field 3.' - example: 'Custom value 3' - custom_value4: - type: string - description: 'Custom value field 4.' - example: 'Custom value 4' - product_key: - type: string - description: 'The product key.' - example: '1234' - notes: - type: string - description: 'Notes about the product.' - example: 'These are some notes about the product.' - cost: - type: number - format: double - description: 'The cost of the product. (Your purchase price for this product)' - example: 10.0 - price: - type: number - format: double - description: 'The price of the product that you are charging.' - example: 20.0 - quantity: - type: number - format: double - description: 'The quantity of the product. (used as a default)' - example: 5.0 - tax_name1: - type: string - description: 'The name of tax 1.' - example: 'Tax 1' - tax_rate1: - type: number - format: double - description: 'The rate of tax 1.' - example: 10.0 - tax_name2: - type: string - description: 'The name of tax 2.' - example: 'Tax 2' - tax_rate2: - type: number - format: double - description: 'The rate of tax 2.' - example: 5.0 - tax_name3: - type: string - description: 'The name of tax 3.' - example: 'Tax 3' - tax_rate3: - type: number - format: double - description: 'The rate of tax 3.' - example: 0.0 - archived_at: - type: integer - format: timestamp - description: 'The timestamp when the product was archived.' - example: '2022-03-18T15:00:00Z' - readOnly: true - created_at: - type: integer - format: timestamp - description: 'The timestamp when the product was created.' - example: '2022-03-18T15:00:00Z' - readOnly: true - updated_at: - description: Timestamp - type: integer - format: timestamp - example: '2022-03-18T12:34:56.789Z' - readOnly: true - is_deleted: - type: boolean - description: 'Boolean flag determining if the product has been deleted' - example: false - readOnly: true - in_stock_quantity: - type: integer - format: int32 - description: The quantity of the product that is currently in stock - default: 0 - stock_notification: - type: boolean - description: Indicates whether stock notifications are enabled for this product - default: true - stock_notification_threshold: - type: integer - format: int32 - description: The minimum quantity threshold for which stock notifications will be triggered - default: 0 - max_quantity: - type: integer - format: int32 - description: The maximum quantity that can be ordered for this product - product_image: - type: string - description: The URL of the product image - format: uri-reference - tax_id: - type: string - default: '1' - description: | - The tax category id for this product.' - - The following constants are available (default = '1') - - ``` - PRODUCT_TYPE_PHYSICAL = '1' - PRODUCT_TYPE_SERVICE = '2' - PRODUCT_TYPE_DIGITAL = '3' - PRODUCT_TYPE_SHIPPING = '4' - PRODUCT_TYPE_EXEMPT = '5' - PRODUCT_TYPE_REDUCED_TAX = '6' - PRODUCT_TYPE_OVERRIDE_TAX = '7' - PRODUCT_TYPE_ZERO_RATED = '8' - PRODUCT_TYPE_REVERSE_TAX = '9' - ``` - example: '1' - - RecurringQuote: - properties: - id: - description: 'The hashed id of the recurring quote' - type: string - example: Opnel5aKBz - user_id: - description: 'The user hashed id' - type: string - example: Opnel5aKBz - assigned_user_id: - description: 'The assigned user hashed id' - type: string - example: Opnel5aKBz - client_id: - description: 'The client hashed id' - type: string - example: Opnel5aKBz - status_id: - description: 'The quote status variable' - type: string - example: '4' - frequency_id: - description: 'The recurring quote frequency' - type: number - example: '4' - remaining_cycles: - description: 'The number of quotes left to be generated' - type: number - example: '4' - number: - description: 'The recurringquote number - is a unique alpha numeric number per quote per company' - type: string - example: INV_101 - po_number: - description: 'The purchase order associated with this recurring quote' - type: string - example: PO-1234 - terms: - description: 'The quote terms' - type: string - example: 'These are quote terms' - public_notes: - description: 'The public notes of the quote' - type: string - example: 'These are some public notes' - private_notes: - description: 'The private notes of the quote' - type: string - example: 'These are some private notes' - footer: - description: 'The quote footer notes' - type: string - example: '' - custom_value1: - description: 'A custom field value' - type: string - example: '2022-10-01' - custom_value2: - description: 'A custom field value' - type: string - example: 'Something custom' - custom_value3: - description: 'A custom field value' - type: string - example: '' - custom_value4: - description: 'A custom field value' - type: string - example: '' - tax_name1: - description: 'The tax name' - type: string - example: '' - tax_name2: - description: 'The tax name' - type: string - example: '' - tax_rate1: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_rate2: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_name3: - description: 'The tax name' - type: string - example: '' - tax_rate3: - description: 'The tax rate' - type: number - format: float - example: '10.00' - total_taxes: - description: 'The total taxes for the quote' - type: number - format: float - example: '10.00' - line_items: - description: 'An array of objects which define the line items of the quote' - type: object - example: '' - amount: - description: 'The quote amount' - type: number - format: float - example: '10.00' - balance: - description: 'The quote balance' - type: number - format: float - example: '10.00' - paid_to_date: - description: 'The amount paid on the quote to date' - type: number - format: float - example: '10.00' - discount: - description: 'The quote discount, can be an amount or a percentage' - type: number - format: float - example: '10.00' - partial: - description: 'The deposit/partial amount' - type: number - format: float - example: '10.00' - is_amount_discount: - description: 'Flag determining if the discount is an amount or a percentage' - type: boolean - example: true - is_deleted: - description: 'Defines if the quote has been deleted' - type: boolean - example: true - uses_inclusive_taxes: - description: 'Defines the type of taxes used as either inclusive or exclusive' - type: boolean - example: true - date: - description: 'The quote Date' - type: string - format: date - example: '1994-07-30' - last_sent_date: - description: 'The last date the quote was sent out' - type: string - format: date - example: '1994-07-30' - next_send_date: - description: 'The Next date for a reminder to be sent' - type: string - format: date - example: '1994-07-30' - partial_due_date: - description: 'The due date for the deposit/partial amount' - type: string - format: date - example: '1994-07-30' - due_date: - description: 'The due date of the quote' - type: string - format: date - example: '1994-07-30' - settings: - $ref: '#/components/schemas/CompanySettings' - last_viewed: - description: Timestamp - type: number - format: integer - example: '1434342123' - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - archived_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - custom_surcharge1: - description: 'First Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge2: - description: 'Second Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge3: - description: 'Third Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge4: - description: 'Fourth Custom Surcharge' - type: number - format: float - example: '10.00' - custom_surcharge_tax1: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - custom_surcharge_tax2: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - custom_surcharge_tax3: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - custom_surcharge_tax4: - description: 'Toggles charging taxes on custom surcharge amounts' - type: boolean - example: true - type: object - Paymentable: - properties: - id: - description: 'The paymentable hashed id' - type: string - example: AS3df3A - invoice_id: - description: 'The invoice hashed id' - type: string - example: AS3df3A - credit_id: - description: 'The credit hashed id' - type: string - example: AS3df3A - refunded: - description: 'The amount that has been refunded for this payment' - type: number - format: float - example: '10.00' - amount: - description: 'The amount that has been applied to the payment' - type: number - format: float - example: '10.00' - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - created_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - type: object - SystemLog: - properties: - id: - description: 'The account hashed id' - type: string - example: AS3df3A - user_id: - description: 'The user_id hashed id' - type: string - example: AS3df3A - client_id: - description: 'The client_id hashed id' - type: string - example: AS3df3A - event_id: - description: 'The Log Type ID' - type: integer - example: 1 - category_id: - description: 'The Category Type ID' - type: integer - example: 1 - type_id: - description: 'The Type Type ID' - type: integer - example: 1 - log: - description: 'The json object of the error' - type: object - example: '{''key'':''value''}' - updated_at: - description: Timestamp - type: string - example: '2' - created_at: - description: Timestamp - type: string - example: '2' - type: object - BankTransaction: - properties: - id: - description: 'The bank integration hashed id' - type: string - example: AS3df3A - user_id: - description: 'The user hashed id' - type: string - example: AS3df3A - transaction_id: - description: 'The id of the transaction rule' - type: integer - example: 343434 - amount: - description: 'The transaction amount' - type: number - example: 10 - currency_id: - description: 'The currency ID of the currency' - type: string - example: '1' - account_type: - description: 'The account type' - type: string - example: creditCard - description: - description: 'The description of the transaction' - type: string - example: 'Potato purchases for kevin' - category_id: - description: 'The category id' - type: integer - example: 1 - category_type: - description: 'The category description' - type: string - example: Expenses - base_type: - description: 'Either CREDIT or DEBIT' - type: string - example: CREDIT - date: - description: 'The date of the transaction' - type: string - example: '2022-09-01' - bank_account_id: - description: 'The ID number of the bank account' - type: integer - example: '1' - type: object - - ProductBulkAction: - required: - - action - - ids - properties: - action: - type: string - example: archive - description: 'The action to perform ie. archive / restore / delete / set_tax_id' - ids: - type: array - items: - format: string - type: string - example: 2J234DFA,D2J234DFA,D2J234DFA - description: string array of client hashed ids - tax_id: - type: string - example: '1' - description: | - The tax rate id to set on the list of products - - The following constants are available (default = '1') - - ``` - PRODUCT_TYPE_PHYSICAL = '1' - PRODUCT_TYPE_SERVICE = '2' - PRODUCT_TYPE_DIGITAL = '3' - PRODUCT_TYPE_SHIPPING = '4' - PRODUCT_TYPE_EXEMPT = '5' - PRODUCT_TYPE_REDUCED_TAX = '6' - PRODUCT_TYPE_OVERRIDE_TAX = '7' - PRODUCT_TYPE_ZERO_RATED = '8' - PRODUCT_TYPE_REVERSE_TAX = '9' - ``` - type: object - FillableInvoice: - properties: - assigned_user_id: - description: "The assigned user's hashed ID" - type: string - example: 'a1b2c3d4' - client_id: - description: "The client's hashed ID" - type: string - example: 'x1y2z3a4' - number: - description: "The unique alphanumeric invoice number for each invoice per company" - type: string - example: INV_101 - po_number: - description: "The purchase order number associated with the invoice" - type: string - example: 'PO12345' - terms: - description: "The terms and conditions for the invoice" - type: string - example: 'Net 30' - public_notes: - description: "Public notes visible to the client on the invoice" - type: string - example: 'Thank you for your business.' - private_notes: - description: "Private notes for internal use only" - type: string - example: 'Client is a slow payer.' - footer: - description: "The footer text displayed on the invoice" - type: string - example: 'Authorized Signature' - custom_value1: - description: "First custom value for additional information" - type: string - example: 'Project ABC' - custom_value2: - description: "Second custom value for additional information" - type: string - example: 'Department XYZ' - custom_value3: - description: "Third custom value for additional information" - type: string - example: 'Location 123' - custom_value4: - description: "Fourth custom value for additional information" - type: string - example: 'Currency USD' - tax_name1: - description: "Name of the first tax applied to the invoice" - type: string - example: 'VAT' - tax_name2: - description: "Name of the second tax applied to the invoice" - type: string - example: 'GST' - tax_rate1: - description: "Rate of the first tax applied to the invoice" - type: number - example: 10.00 - tax_rate2: - description: "Rate of the second tax applied to the invoice" - type: number - example: 5.00 - tax_name3: - description: "Name of the third tax applied to the invoice" - type: string - example: 'PST' - tax_rate3: - description: "Rate of the third tax applied to the invoice" - type: number - example: 8.00 - line_items: - type: array - description: 'An array of objects which define the line items of the invoice' - items: - $ref: '#/components/schemas/InvoiceItem' - discount: - description: "The discount applied to the invoice" - type: number - example: 10.00 - partial: - description: "The partial amount applied to the invoice" - type: number - example: 20.00 - is_amount_discount: - description: "Indicates whether the discount applied is a fixed amount or a percentage" - type: boolean - example: true - uses_inclusive_taxes: - description: "Indicates whether the tax rates applied to the invoice are inclusive or exclusive" - type: boolean - example: true - date: - description: "The date the invoice was issued" - type: string - example: '1994-07-30' - partial_due_date: - description: "The due date for the partial payment" - type: string - example: '1994-08-15' - due_date: - description: "The due date for the invoice" - type: string - example: '1994-08-30' - custom_surcharge1: - description: "First custom surcharge applied to the invoice" - type: number - example: 10.00 - custom_surcharge2: - description: "Second custom surcharge applied to the invoice" - type: number - example: 15.00 - custom_surcharge3: - description: "Third custom surcharge applied to the invoice" - type: number - example: 5.00 - custom_surcharge4: - description: "Fourth custom surcharge applied to the invoice" - type: number - example: 20.00 - type: object - CompanyGateway: - properties: - id: - description: 'The hashed id of the company gateway' - type: string - example: Opnel5aKBz - gateway_key: - description: 'The gateway key (hash)' - type: string - example: '2' - accepted_credit_cards: - description: 'Bitmask representation of cards' - type: integer - example: '32' - require_billing_address: - description: 'Determines if the the billing address is required prior to payment.' - type: boolean - example: true - require_shipping_address: - description: 'Determines if the the billing address is required prior to payment.' - type: boolean - example: true - config: - description: 'The configuration map for the gateway' - type: string - example: dfadsfdsafsafd - update_details: - description: 'Determines if the client details should be updated.' - type: boolean - example: true - fees_and_limits: - description: 'A mapped collection of the fees and limits for the configured gateway' - type: array - items: - $ref: '#/components/schemas/FeesAndLimits' - type: object - - - Activity: - properties: - id: - description: 'The id field of the activity' - type: string - example: Opnel5aKBz - activity_type_id: - description: 'The activity type id' - type: string - example: Opnel5aKBz - client_id: - description: 'The client hashed id' - type: string - example: Opnel5aKBz - company_id: - description: 'The company hashed id' - type: string - example: Opnel5aKBz - user_id: - description: 'The user hashed id' - type: string - example: Opnel5aKBz - invoice_id: - description: 'The invoice hashed id' - type: string - example: Opnel5aKBz - payment_id: - description: 'The payment hashed id' - type: string - example: Opnel5aKBz - credit_id: - description: 'The credit hashed id' - type: string - example: Opnel5aKBz - updated_at: - description: 'Unixtimestamp the last time the record was updated' - type: integer - example: '343421434' - expense_id: - description: 'The expense hashed id' - type: string - example: Opnel5aKBz - is_system: - description: 'Defines is the activity was performed by the system' - type: boolean - example: true - contact_id: - description: 'The contact hashed id' - type: string - example: Opnel5aKBz - task_id: - description: 'The task hashed id' - type: string - example: Opnel5aKBz - notes: - description: 'Activity Notes' - type: string - example: Opnel5aKBz - token_id: - description: 'The hashed ID of the token who performed the action' - type: string - example: Opnel5aKBz - ip: - description: 'The IP Address of the user who performed the action' - type: string - example: 192.168.1.252 - user: - $ref: '#/components/schemas/User' - client: - $ref: '#/components/schemas/Client' - contact: - $ref: '#/components/schemas/ClientContact' - recurring_invoice: - $ref: '#/components/schemas/RecurringInvoice' - invoice: - $ref: '#/components/schemas/Invoice' - credit: - $ref: '#/components/schemas/Credit' - quote: - $ref: '#/components/schemas/Quote' - payment: - $ref: '#/components/schemas/Payment' - expense: - $ref: '#/components/schemas/Expense' - task: - $ref: '#/components/schemas/Task' - purchase_order: - $ref: '#/components/schemas/PurchaseOrder' - vendor: - $ref: '#/components/schemas/Vendor' - vendor_contact: - $ref: '#/components/schemas/VendorContact' - type: object - User: - properties: - id: - description: 'The hashed id of the user' - type: string - example: Opnel5aKBz - readOnly: true - first_name: - description: 'The first name of the user' - type: string - example: Brad - last_name: - description: 'The last name of the user' - type: string - example: Pitt - email: - description: 'The users email address' - type: string - example: brad@pitt.com - phone: - description: 'The users phone number' - type: string - example: 555-1233-23232 - signature: - description: 'The users sign off signature' - type: string - example: 'Have a nice day!' - avatar: - description: 'The users avatar' - type: string - example: 'https://url.to.your/avatar.png' - accepted_terms_version: - description: 'The version of the invoice ninja terms that has been accepted by the user' - type: string - example: 1.0.1 - readOnly: true - oauth_user_id: - description: 'The provider id of the oauth entity' - type: string - example: jkhasdf789as6f675sdf768sdfs - readOnly: true - oauth_provider_id: - description: 'The oauth entity id' - type: string - example: google - readOnly: true - language_id: - description: 'The language id of the user' - type: string - example: 1 - verified_phone_number: - description: 'Boolean flag if the user has their phone verified. Required to settings up 2FA' - type: boolean - example: true - readOnly: true - sms_verification_code: - description: 'The sms verification code for the user. Required to settings up 2FA' - type: string - example: '123456' - readOnly: true - oauth_user_token_expiry: - description: 'The expiry date of the oauth token' - type: string - example: '2022-10-10' - readOnly: true - has_password: - description: 'Boolean flag determining if the user has a password' - type: boolean - example: true - readOnly: true - last_confirmed_email_address: - description: 'The last confirmed email address of the user' - type: string - example: 'bob@gmail.com' - readOnly: true - custom_value1: - description: 'A custom value' - type: string - example: 'Custom value 1' - custom_value2: - description: 'A custom value' - type: string - example: '$1000' - custom_value3: - description: 'A custom value' - type: string - example: 'Custom value 3' - custom_value4: - description: 'A custom value' - type: string - example: 'Custom value 4' - is_deleted: - description: 'Boolean flag determining if the user has been deleted' - type: boolean - example: true - readOnly: true - google_2fa_secret: - description: 'The google 2fa secret for the user' - type: string - example: '123456' - readOnly: true - company_user: - $ref: '#/components/schemas/CompanyUserRef' - type: object - UserRef: - properties: - id: - description: 'The hashed id of the user' - type: string - example: Opnel5aKBz - readOnly: true - first_name: - description: 'The first name of the user' - type: string - example: Brad - last_name: - description: 'The last name of the user' - type: string - example: Pitt - email: - description: 'The users email address' - type: string - example: brad@pitt.com - phone: - description: 'The users phone number' - type: string - example: 555-1233-23232 - signature: - description: 'The users sign off signature' - type: string - example: 'Have a nice day!' - avatar: - description: 'The users avatar' - type: string - example: 'https://url.to.your/avatar.png' - accepted_terms_version: - description: 'The version of the invoice ninja terms that has been accepted by the user' - type: string - example: 1.0.1 - readOnly: true - oauth_user_id: - description: 'The provider id of the oauth entity' - type: string - example: jkhasdf789as6f675sdf768sdfs - readOnly: true - oauth_provider_id: - description: 'The oauth entity id' - type: string - example: google - readOnly: true - language_id: - description: 'The language id of the user' - type: string - example: 1 - verified_phone_number: - description: 'Boolean flag if the user has their phone verified. Required to settings up 2FA' - type: boolean - example: true - readOnly: true - sms_verification_code: - description: 'The sms verification code for the user. Required to settings up 2FA' - type: string - example: '123456' - readOnly: true - oauth_user_token_expiry: - description: 'The expiry date of the oauth token' - type: string - example: '2022-10-10' - readOnly: true - has_password: - description: 'Boolean flag determining if the user has a password' - type: boolean - example: true - readOnly: true - last_confirmed_email_address: - description: 'The last confirmed email address of the user' - type: string - example: 'bob@gmail.com' - readOnly: true - custom_value1: - description: 'A custom value' - type: string - example: 'Custom value 1' - custom_value2: - description: 'A custom value' - type: string - example: '$1000' - custom_value3: - description: 'A custom value' - type: string - example: 'Custom value 3' - custom_value4: - description: 'A custom value' - type: string - example: 'Custom value 4' - is_deleted: - description: 'Boolean flag determining if the user has been deleted' - type: boolean - example: true - readOnly: true - google_2fa_secret: - description: 'The google 2fa secret for the user' - type: string - example: '123456' - readOnly: true - type: object - CompanyUser: - properties: - permissions: - description: 'The user permissionsfor this company in a comma separated list' - type: string - example: 'create_invoice,create_client,view_client' - settings: - description: 'Settings that are used for the flutter applications to store user preferences / metadata' - type: object - readOnly: true - react_settings: - description: 'Dedicated settings object for the react web application' - type: object - readOnly: true - is_owner: - description: 'Determines whether the user owns this company' - type: boolean - example: true - readOnly: true - is_admin: - description: 'Determines whether the user is the admin of this company' - type: boolean - example: true - readOnly: true - is_locked: - description: 'Determines whether the users access to this company has been locked' - type: boolean - example: true - readOnly: true - updated_at: - description: 'The last time the record was modified, format Unix Timestamp' - type: integer - example: '1231232312321' - deleted_at: - description: 'Timestamp when the user was archived, format Unix Timestamp' - type: integer - example: '12312312321' - account: - $ref: '#/components/schemas/Account' - company: - $ref: '#/components/schemas/Company' - user: - $ref: '#/components/schemas/UserRef' - token: - $ref: '#/components/schemas/CompanyToken' - type: object - CompanyUserRef: - properties: - permissions: - description: 'The user permissionsfor this company in a comma separated list' - type: string - example: 'create_invoice,create_client,view_client' - settings: - description: 'Settings that are used for the flutter applications to store user preferences / metadata' - type: object - readOnly: true - react_settings: - description: 'Dedicated settings object for the react web application' - type: object - readOnly: true - is_owner: - description: 'Determines whether the user owns this company' - type: boolean - example: true - readOnly: true - is_admin: - description: 'Determines whether the user is the admin of this company' - type: boolean - example: true - readOnly: true - is_locked: - description: 'Determines whether the users access to this company has been locked' - type: boolean - example: true - readOnly: true - updated_at: - description: 'The last time the record was modified, format Unix Timestamp' - type: integer - example: '1231232312321' - deleted_at: - description: 'Timestamp when the user was archived, format Unix Timestamp' - type: integer - example: '12312312321' - account: - $ref: '#/components/schemas/Account' - company: - $ref: '#/components/schemas/Company' - user: - $ref: '#/components/schemas/UserRef' - token: - $ref: '#/components/schemas/CompanyToken' - type: object - RecurringExpense: - properties: - id: - description: 'The hashed id of the recurring expense' - type: string - example: Opnel5aKBz - user_id: - description: 'The hashed id of the user who created the recurring expense' - type: string - example: Opnel5aKBz - assigned_user_id: - description: 'The hashed id of the user assigned to this recurring expense' - type: string - example: Opnel5aKBz - client_id: - description: 'The hashed id of the client' - type: string - example: Opnel5aKBz - invoice_id: - description: 'The hashed id of the invoice' - type: string - example: Opnel5aKBz - bank_id: - description: 'The id of the bank associated with this recurring expense' - type: string - example: '22' - invoice_currency_id: - description: 'The currency id of the invoice associated with this recurring expense' - type: string - example: '1' - expense_currency_id: - description: 'The currency id of the expense associated with this recurring expense' - type: string - example: '1' - invoice_category_id: - description: 'The category id of the invoice' - type: string - example: '1' - payment_type_id: - description: 'The payment type id' - type: string - example: '1' - private_notes: - description: 'The recurring expense private notes' - type: string - example: 'Private and confidential' - public_notes: - description: 'The recurring expense public notes' - type: string - example: 'This is the best client in the world' - transaction_reference: - description: 'The recurring expense transaction reference' - type: string - example: EXP-1223-2333 - transcation_id: - description: 'The transaction id of the recurring expense' - type: string - example: '1233312312' - custom_value1: - description: 'Custom value field' - type: string - example: $1000 - custom_value2: - description: 'Custom value field' - type: string - example: '2022-10-10' - custom_value3: - description: 'Custom value field' - type: string - example: 'short text' - custom_value4: - description: 'Custom value field' - type: string - example: 'very long text' - tax_name1: - description: 'The tax name' - type: string - example: GST - tax_name2: - description: 'The tax name' - type: string - example: VAT - tax_rate1: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_rate2: - description: 'The tax rate' - type: number - format: float - example: '10.00' - tax_name3: - description: 'The tax name' - type: string - example: '' - tax_rate3: - description: 'The tax rate' - type: number - format: float - example: '10.00' - amount: - description: 'The total amount of the recurring expense' - type: number - format: float - example: '10.00' - frequency_id: - description: 'The frequency this recurring expense fires' - type: number - format: int - example: '1' - remaining_cycles: - description: 'The number of remaining cycles for this recurring expense' - type: number - format: int - example: '1' - foreign_amount: - description: 'The foreign currency amount of the recurring expense' - type: number - format: float - example: '10.00' - exchange_rate: - description: 'The exchange rate for the expernse' - type: number - format: float - example: '0.80' - date: - description: 'The date of the expense' - type: string - example: '' - payment_date: - description: 'The date the expense was paid' - type: string - example: '' - should_be_invoiced: - description: 'Boolean flag determining if the expense should be invoiced' - type: boolean - example: true - is_deleted: - description: 'Boolean flag determining if the recurring expense is deleted' - type: boolean - example: true - last_sent_date: - description: 'The Date it was sent last' - type: string - format: date - example: '1994-07-30' - next_send_date: - description: 'The next send date' - type: string - format: date - example: '1994-07-30' - invoice_documents: - description: 'Boolean flag determining if the documents associated with this expense should be passed onto the invoice if it is converted to an invoice' - type: boolean - example: true - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - archived_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - type: object ClientContact: properties: id: @@ -21509,275 +21644,6 @@ components: example: '134341234234' readOnly: true type: object - Expense: - properties: - id: - description: 'The expense hashed id' - type: string - example: 'Opnel5aKBz' - user_id: - description: 'The user hashed id' - type: string - example: 'Opnel5aKBz' - assigned_user_id: - description: 'The assigned user hashed id' - type: string - example: 'Opnel5aKBz' - project_id: - description: 'The associated project_id' - type: string - example: 'Opnel5aKBz' - client_id: - description: 'The client hashed id' - type: string - example: 'Opnel5aKBz' - invoice_id: - description: 'The related invoice hashed id' - type: string - example: 'Opnel5aKBz' - bank_id: - description: 'The bank id related to this expense' - type: string - example: '' - invoice_currency_id: - description: 'The currency id of the related invoice' - type: string - example: '1' - currency_id: - description: 'The currency id of the expense' - type: string - example: '2' - invoice_category_id: - description: 'The invoice category id' - type: string - example: 'Opnel5aKBz' - payment_type_id: - description: 'The payment type id' - type: string - example: '' - recurring_expense_id: - description: 'The related recurring expense this expense was created from' - type: string - example: 'Opnel5aKBz' - private_notes: - description: 'The private notes of the expense' - type: string - example: '' - public_notes: - description: 'The public notes of the expense' - type: string - example: '' - transaction_reference: - description: 'The transaction references of the expense' - type: string - example: '' - transcation_id: - description: 'The transaction id of the expense' - type: string - example: '' - custom_value1: - description: 'A custom value' - type: string - example: '' - custom_value2: - description: 'A custom value' - type: string - example: '' - custom_value3: - description: 'A custom value' - type: string - example: '' - custom_value4: - description: 'A custom value' - type: string - example: '' - tax_amount: - description: 'The tax amount' - type: number - example: 10.00 - tax_name1: - description: 'Tax Name 1' - type: string - example: 'GST' - tax_name2: - description: 'Tax Name 2' - type: string - example: 'VAT' - tax_name3: - description: 'Tax Name 3' - type: string - example: 'IVA' - tax_rate1: - description: 'Tax rate 1' - type: number - format: float - example: '10.00' - tax_rate2: - description: 'Tax rate 2' - type: number - format: float - example: '10.00' - tax_rate3: - description: 'Tax rate 3' - type: number - format: float - example: '10.00' - amount: - description: 'The total expense amont' - type: number - format: float - example: '10.00' - foreign_amount: - description: 'The total foreign amount of the expense' - type: number - format: float - example: '10.00' - exchange_rate: - description: 'The exchange rate at the time of the expense' - type: number - format: float - example: '0.80' - date: - description: 'The expense date format Y-m-d' - type: string - example: '2022-12-01' - payment_date: - description: 'The date of payment for the expense, format Y-m-d' - type: string - example: '2022-12-01' - should_be_invoiced: - description: 'Flag whether the expense should be invoiced' - type: boolean - example: true - is_deleted: - description: 'Boolean determining whether the expense has been deleted' - type: boolean - example: true - invoice_documents: - description: 'Passing the expense documents over to the invoice' - type: boolean - example: true - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - archived_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - type: object - Task: - properties: - id: - description: 'The hashed id of the task' - type: string - example: Opnel5aKBz - user_id: - description: 'The hashed id of the user who created the task' - type: string - example: Opnel5aKBz - assigned_user_id: - description: 'The assigned user of the task' - type: string - example: Opnel5aKBz - client_id: - description: 'The hashed if of the client' - type: string - example: Opnel5aKBz - invoice_id: - description: 'The hashed id of the invoice associated with the task' - type: string - example: Opnel5aKBz - project_id: - description: 'The hashed id of the project associated with the task' - type: string - example: Opnel5aKBz - number: - description: 'The number of the task' - type: string - example: TASK-123 - time_log: - description: 'An array of unix time stamps defining the start and end times of the task' - type: string - example: '[[1,2],[3,4]]' - is_running: - description: 'Determines if the task is still running' - type: boolean - example: true - is_deleted: - description: 'Boolean flag determining if the task has been deleted' - type: boolean - example: true - task_status_id: - description: 'The hashed id of the task status' - type: string - example: Opnel5aKBz - description: - description: 'The task description' - type: string - example: 'A wonder task to work on' - duration: - description: 'The task duration in seconds' - type: integer - example: '3600' - task_status_order: - description: 'The order of the task' - type: integer - example: '4' - rate: - description: 'The task rate' - type: number - example: 10.00 - custom_value1: - description: 'A custom value' - type: string - example: '2022-10-10' - custom_value2: - description: 'A custom value' - type: string - example: $1100 - custom_value3: - description: 'A custom value' - type: string - example: 'I need help' - custom_value4: - description: 'A custom value' - type: string - example: INV-3343 - is_date_based: - description: 'Boolean flag determining if the task is date based' - type: boolean - example: true - calculated_start_date: - description: 'The calculated start date of the task' - type: string - example: '2022-10-10' - readOnly: true - invoice_documents: - description: "Boolean flags which determines whether to include the task documents on the invoice" - type: boolean - example: true - created_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - readOnly: true - updated_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - readOnly: true - archived_at: - description: Timestamp - type: number - format: integer - example: '1434342123' - readOnly: true - type: object FeesAndLimits: properties: @@ -21836,150 +21702,327 @@ components: type: boolean example: true type: object - Vendor: + Account: properties: id: - description: 'The hashed id of the vendor. This is a unique identifier for the vendor.' + description: 'The account hashed id' type: string - example: Opnel5aKBz - readOnly: true + example: AS3df3A + account_sms_verified: + description: 'Boolean flag if the account has been verified by sms' + type: string + example: true + type: object + BankTransactionRule: + properties: + id: + description: 'The bank transaction rules hashed id' + type: string + example: AS3df3A user_id: - description: 'The hashed id of the user who created the vendor. This is a unique identifier for the user.' + description: 'The user hashed id' type: string - example: Opnel5aKBz - assigned_user_id: - description: 'The hashed id of the assigned user to this vendor. This is a unique identifier for the user.' + example: AS3df3A + name: + description: 'The name of the transaction' type: string - example: Opnel5aKBz - contacts: + example: 'Rule 1' + rules: + description: 'A mapped collection of the sub rules for the BankTransactionRule' type: array items: - $ref: '#/components/schemas/VendorContact' - description: 'An array of contacts associated with the vendor.' - name: - description: 'The name of the vendor.' - type: string - example: 'Harry cafe de wheels' - classification: - description: 'The classification of the vendor.' - type: string - example: 'individual' - website: - description: 'The website of the vendor.' - type: string - example: www.harry.com - private_notes: - description: 'The private notes of the vendor. These notes are only visible to users with appropriate permissions.' - type: string - example: 'Shhh, do not tell the vendor' - industry_id: - description: 'The industry id of the vendor. This is a unique identifier for the industry.' - type: string - example: '1' - size_id: - description: 'The size id of the vendor. This is a unique identifier for the size of the vendor.' - type: string - example: '' - address1: - description: 'The first line of the vendor''s address.' - type: string - example: '' - address2: - description: 'The second line of the vendor''s address.' - type: string - example: '' - city: - description: 'The city of the vendor''s address.' - type: string - example: '' - state: - description: 'The state of the vendor''s address.' - type: string - example: '' - postal_code: - description: 'The postal code of the vendor''s address.' - type: string - example: '' - phone: - description: 'The phone number of the vendor.' - type: string - example: 555-3434-3434 - country_id: - description: 'The country id of the vendor. This is a unique identifier for the country.' - type: string - example: '' - currency_id: - description: 'The currency id of the vendor. This is a unique identifier for the currency.' - type: string - example: '4' - custom_value1: - description: 'The value of the first custom field for the vendor.' - type: string - example: '' - custom_value2: - description: 'The value of the second custom field for the vendor.' - type: string - example: '' - custom_value3: - description: 'The value of the third custom field for the vendor.' - type: string - example: '' - custom_value4: - description: 'The value of the fourth custom field for the vendor.' - type: string - example: '' - vat_number: - description: 'The VAT number of the vendor.' - type: string - example: '' - id_number: - description: 'The ID number of the vendor.' - type: string - example: '' - number: - description: 'The number of the vendor' - type: string - example: '11234' - is_deleted: - description: 'Boolean flag determining if the vendor has been deleted' + $ref: '#/components/schemas/BTRules' + auto_convert: + description: 'Flags whether the rule converts the transaction automatically' type: boolean example: true - language_id: - description: 'The language id of the vendor. This is a unique identifier for the language.' + matches_on_all: + description: 'Flags whether all subrules are required for the match' + type: boolean + example: true + applies_to: + description: 'Flags whether the rule applies to a CREDIT or DEBIT' type: string - example: '1' - vendor_hash: - description: 'The vendor hash of the vendor. This is a unique identifier for the vendor.' + example: CREDIT + client_id: + description: 'The client hashed id' type: string - example: 'aaa-sss-www' + example: AS3df3A + vendor_id: + description: 'The vendor hashed id' + type: string + example: AS3df3A + category_id: + description: 'The category hashed id' + type: string + example: AS3df3A + type: object + CompanyToken: + properties: + name: + description: 'The token name' + type: string + example: 'Token Name' + token: + description: 'The token value' + type: string + example: AS3df3jUUH765fhfd9KJuidj3JShjA + is_system: + description: 'Determines whether the token is created by the system rather than a user' + type: boolean + example: 'true' + type: object + CompanyUser: + properties: + permissions: + description: 'The user permissionsfor this company in a comma separated list' + type: string + example: 'create_invoice,create_client,view_client' + settings: + description: 'Settings that are used for the flutter applications to store user preferences / metadata' + type: object readOnly: true - transaction_name: - description: 'The transaction name of the vendor.' + react_settings: + description: 'Dedicated settings object for the react web application' + type: object + readOnly: true + is_owner: + description: 'Determines whether the user owns this company' + type: boolean + example: true + readOnly: true + is_admin: + description: 'Determines whether the user is the admin of this company' + type: boolean + example: true + readOnly: true + is_locked: + description: 'Determines whether the users access to this company has been locked' + type: boolean + example: true + readOnly: true + updated_at: + description: 'The last time the record was modified, format Unix Timestamp' + type: integer + example: '1231232312321' + deleted_at: + description: 'Timestamp when the user was archived, format Unix Timestamp' + type: integer + example: '12312312321' + account: + $ref: '#/components/schemas/Account' + company: + $ref: '#/components/schemas/Company' + user: + $ref: '#/components/schemas/UserRef' + token: + $ref: '#/components/schemas/CompanyToken' + type: object + CompanyUserRef: + properties: + permissions: + description: 'The user permissionsfor this company in a comma separated list' type: string - example: 'aaa-sss-www' - last_login: - description: Timestamp + example: 'create_invoice,create_client,view_client' + settings: + description: 'Settings that are used for the flutter applications to store user preferences / metadata' + type: object + readOnly: true + react_settings: + description: 'Dedicated settings object for the react web application' + type: object + readOnly: true + is_owner: + description: 'Determines whether the user owns this company' + type: boolean + example: true + readOnly: true + is_admin: + description: 'Determines whether the user is the admin of this company' + type: boolean + example: true + readOnly: true + is_locked: + description: 'Determines whether the users access to this company has been locked' + type: boolean + example: true + readOnly: true + updated_at: + description: 'The last time the record was modified, format Unix Timestamp' + type: integer + example: '1231232312321' + deleted_at: + description: 'Timestamp when the user was archived, format Unix Timestamp' + type: integer + example: '12312312321' + account: + $ref: '#/components/schemas/Account' + company: + $ref: '#/components/schemas/Company' + user: + $ref: '#/components/schemas/UserRef' + token: + $ref: '#/components/schemas/CompanyToken' + type: object + InvoiceInvitationRequest: + required: + - client_contact_id + properties: + id: + description: 'The entity invitation hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + key: + description: 'The invitation key' + type: string + example: Opnel5aKBz4343343566236gvbb + readOnly: true + link: + description: 'The invitation link' + type: string + example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' + readOnly: true + sent_date: + description: 'The invitation sent date' + type: string + format: date-time + readOnly: true + viewed_date: + description: 'The invitation viewed date' + type: string + format: date-time + readOnly: true + opened_date: + description: 'The invitation opened date' + type: string + format: date-time + readOnly: true + updated_at: + description: 'Timestamp' type: number format: integer - example: '134341234234' + example: '1434342123' readOnly: true + archived_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + email_error: + description: 'The email error' + type: string + example: 'The email error' + readOnly: true + email_status: + description: 'The email status' + type: string + readOnly: true + + InvoiceInvitation: + properties: + id: + description: 'The entity invitation hashed id' + type: string + example: Opnel5aKBz + readOnly: true + client_contact_id: + description: 'The client contact hashed id' + type: string + example: Opnel5aKBz + key: + description: 'The invitation key' + type: string + example: Opnel5aKBz4343343566236gvbb + readOnly: true + link: + description: 'The invitation link' + type: string + example: 'https://www.example.com/invitations/Opnel5aKBz4343343566236gvbb' + readOnly: true + sent_date: + description: 'The invitation sent date' + type: string + format: date-time + readOnly: true + viewed_date: + description: 'The invitation viewed date' + type: string + format: date-time + readOnly: true + opened_date: + description: 'The invitation opened date' + type: string + format: date-time + readOnly: true + updated_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + archived_at: + description: 'Timestamp' + type: number + format: integer + example: '1434342123' + readOnly: true + email_error: + description: 'The email error' + type: string + example: 'The email error' + readOnly: true + email_status: + description: 'The email status' + type: string + readOnly: true + + Design: + properties: + id: + description: 'The design hashed id' + type: string + example: AS3df3A + name: + description: 'The design name' + type: string + example: Beauty + design: + description: 'The design HTML' + type: string + example: '' + is_custom: + description: 'Flag to determine if the design is a custom user design' + type: boolean + example: true + is_active: + description: 'Flag to determine if the design is available for use' + type: boolean + example: true + is_deleted: + description: 'Flag to determine if the design is deleted' + type: boolean + example: true created_at: description: Timestamp type: number format: integer example: '134341234234' - readOnly: true updated_at: description: Timestamp type: number format: integer example: '134341234234' - readOnly: true - display_name: - description: 'The display name of the vendor.' - type: string - example: 'Bob the vendor' - readOnly: true + deleted_at: + description: Timestamp + type: number + format: integer + example: '134341234234' type: object tags: - name: login @@ -21989,6 +22032,7 @@ tags: description: "Find out more" url: "https://invoiceninja.github.io" - name: clients + x-tag-expanded: true description: | Endpoint definitions for interacting with clients. - name: products @@ -21996,7 +22040,26 @@ tags: Endpoint definitions for interacting with products. - name: invoices description: | - Endpoint definitions for interacting with invoices. + + ## Invoice Statuses + + - STATUS_DRAFT = 1 + - Draft invoices are not visible in the client portal, are not active and they do not impact the client balance + - STATUS_SENT = 2 + - Send invoices are visible in the client portal, are active and impact the client balance + - STATUS_PARTIAL = 3 + - An invoice which has been partially paid + - STATUS_PAID = 4 + - An invoice which has been fully paid + - STATUS_CANCELLED = 5 + - When a invoice is cancelled the invoice balance is set to zero and the status updated to Cancelled, the client balance is also adjusted down. If payments have been applied to the invoice these will remain linked to the invoice + - STATUS_REVERSED = 6 + - When a invoice has been reversed, the previous payments relationships are unlinked and cannot be restored + - STATUS_OVERDUE = -1 + - An invoice which is still payable past its due date + - STATUS_UNPAID = -2 + - An invoice which is not yet due, but also, not paid + - name: Recurring Invoices description: | Endpoint definitions for interacting with recurring_invoices. diff --git a/openapi/components/schemas/company.yaml b/openapi/components/schemas/company.yaml index 50f1c05a287e..01388dddadef 100644 --- a/openapi/components/schemas/company.yaml +++ b/openapi/components/schemas/company.yaml @@ -57,7 +57,44 @@ type: boolean example: true custom_fields: - description: "A mapping of custom fields for various objects within the company" + description: | + A mapping of custom fields for various objects within the company + + **note** + The custom fields object is formatted as follows + + key : value + entity(integer) : label|type + + for example: + "company1": "Number|single_line_text", + + This defines the first company custom field, with label Number which has a custom field type of a single text line + + Supported entity types + + invoice + product + client + contact + task + user + project + vendor + expense + payment + + Supported input types values + + single_line_text + date + switch + + For text areas, you only need to supply the label ie "TextArea", the | and type values are not required. + For the dropdown the data format is as follows: + + label|your,drop,down,values,in,comma,separated,format + type: object enable_product_cost: description: "A flag determining whether to show or hide the product cost field in the user interface" diff --git a/openapi/misc/misc.yaml b/openapi/misc/misc.yaml index 6d7f3194ba4f..c7c101693e43 100644 --- a/openapi/misc/misc.yaml +++ b/openapi/misc/misc.yaml @@ -6,6 +6,7 @@ tags: description: "Find out more" url: "https://invoiceninja.github.io" - name: clients + x-tag-expanded: true description: | Endpoint definitions for interacting with clients. - name: products @@ -13,7 +14,26 @@ tags: Endpoint definitions for interacting with products. - name: invoices description: | - Endpoint definitions for interacting with invoices. + + ## Invoice Statuses + + - STATUS_DRAFT = 1 + - Draft invoices are not visible in the client portal, are not active and they do not impact the client balance + - STATUS_SENT = 2 + - Send invoices are visible in the client portal, are active and impact the client balance + - STATUS_PARTIAL = 3 + - An invoice which has been partially paid + - STATUS_PAID = 4 + - An invoice which has been fully paid + - STATUS_CANCELLED = 5 + - When a invoice is cancelled the invoice balance is set to zero and the status updated to Cancelled, the client balance is also adjusted down. If payments have been applied to the invoice these will remain linked to the invoice + - STATUS_REVERSED = 6 + - When a invoice has been reversed, the previous payments relationships are unlinked and cannot be restored + - STATUS_OVERDUE = -1 + - An invoice which is still payable past its due date + - STATUS_UNPAID = -2 + - An invoice which is not yet due, but also, not paid + - name: Recurring Invoices description: | Endpoint definitions for interacting with recurring_invoices. diff --git a/package-lock.json b/package-lock.json index 3c1cb2273643..2cda57f4cf43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "packages": { "": { "dependencies": { - "@invoiceninja/simple-card": "^0.0.2", "axios": "^0.25", "card-js": "^1.0.13", "card-validator": "^8.1.1", @@ -2026,15 +2025,6 @@ "purgecss": "^3.1.3" } }, - "node_modules/@invoiceninja/simple-card": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@invoiceninja/simple-card/-/simple-card-0.0.2.tgz", - "integrity": "sha512-xDZvfrumnE7Qkp5e4N8EFfEIOcQfMJXSI+o/xeVlTb1WvibulSBgWkIg7J0zZW0eIDvGKCpEv3k+NBpASlaJUw==", - "dependencies": { - "@maskito/core": "^3.0.0", - "@maskito/kit": "^3.0.0" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2200,19 +2190,6 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, - "node_modules/@maskito/core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.0.0.tgz", - "integrity": "sha512-g7zeYPMlpMczrq4Huf+Bpdm3Emy/GO0NUXXnQnUiCjlAoKQl+86cLyP5Hbf4HGcNl/J9SoEGEA4uoW6uUc/yLw==" - }, - "node_modules/@maskito/kit": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.0.0.tgz", - "integrity": "sha512-aXRlDBjeNox/+D7hbXtnM9INGml1QUIXhrnScrCsbqgg7550mt/ivh4PrxL7oazq/BH7HhvS4olJCF5TPEti1g==", - "peerDependencies": { - "@maskito/core": "^3.0.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -12735,15 +12712,6 @@ "purgecss": "^3.1.3" } }, - "@invoiceninja/simple-card": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@invoiceninja/simple-card/-/simple-card-0.0.2.tgz", - "integrity": "sha512-xDZvfrumnE7Qkp5e4N8EFfEIOcQfMJXSI+o/xeVlTb1WvibulSBgWkIg7J0zZW0eIDvGKCpEv3k+NBpASlaJUw==", - "requires": { - "@maskito/core": "^3.0.0", - "@maskito/kit": "^3.0.0" - } - }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -12868,17 +12836,6 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, - "@maskito/core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@maskito/core/-/core-3.0.0.tgz", - "integrity": "sha512-g7zeYPMlpMczrq4Huf+Bpdm3Emy/GO0NUXXnQnUiCjlAoKQl+86cLyP5Hbf4HGcNl/J9SoEGEA4uoW6uUc/yLw==" - }, - "@maskito/kit": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-3.0.0.tgz", - "integrity": "sha512-aXRlDBjeNox/+D7hbXtnM9INGml1QUIXhrnScrCsbqgg7550mt/ivh4PrxL7oazq/BH7HhvS4olJCF5TPEti1g==", - "requires": {} - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index 43bb262ed960..a0edb0de1d2e 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "vue-template-compiler": "^2.6.14" }, "dependencies": { - "@invoiceninja/simple-card": "^0.0.2", "axios": "^0.25", "card-js": "^1.0.13", "card-validator": "^8.1.1", diff --git a/phpstan.neon b/phpstan.neon index aeb4c03e7948..f0eab2441b2c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -38,5 +38,4 @@ parameters: - '#Left side of && is always true.#' - '#Right side of && is always true.#' - '#is never read, only written.#' - - '#is never written#' - \ No newline at end of file + - '#is never written#' \ No newline at end of file diff --git a/public/build/assets/app-fee1da41.css b/public/build/assets/app-23f93261.css similarity index 58% rename from public/build/assets/app-fee1da41.css rename to public/build/assets/app-23f93261.css index 5ca74282105c..2b0a6087f862 100644 --- a/public/build/assets/app-fee1da41.css +++ b/public/build/assets/app-23f93261.css @@ -1 +1 @@ -*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Open Sans,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],input:where(:not([type])),[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,input:where(:not([type])):focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media (forced-colors: active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.form-input,.form-textarea,.form-select,.form-multiselect{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}.form-input:focus,.form-textarea:focus,.form-select:focus,.form-multiselect:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}.form-select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}.form-select:where([size]:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}.form-checkbox,.form-radio{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}.form-checkbox{border-radius:0}.form-radio{border-radius:100%}.form-checkbox:focus,.form-radio:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.form-checkbox:checked,.form-radio:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}.form-checkbox:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media (forced-colors: active){.form-checkbox:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}.form-radio:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media (forced-colors: active){.form-radio:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}.form-checkbox:checked:hover,.form-checkbox:checked:focus,.form-radio:checked:hover,.form-radio:checked:focus{border-color:transparent;background-color:currentColor}.form-checkbox:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media (forced-colors: active){.form-checkbox:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}.form-checkbox:indeterminate:hover,.form-checkbox:indeterminate:focus{border-color:transparent;background-color:currentColor}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.button{border-radius:.25rem;padding:.75rem 1rem;font-size:.875rem;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}button:disabled{cursor:not-allowed;opacity:.5}.button-primary{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.button-primary:hover{font-weight:600}.button-block{display:block;width:100%}.button-danger{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.button-danger:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.button-secondary{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.button-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.button-link{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.button-link:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity));text-decoration-line:underline}.button-link:focus{text-decoration-line:underline;outline:2px solid transparent;outline-offset:2px}.validation{margin-top:.5rem;margin-bottom:.25rem;border-left-width:2px;--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity));padding:.25rem .75rem}.validation-fail{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.validation-pass{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.input{margin-top:.5rem;align-items:center;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.input:focus{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity));outline:2px solid transparent;outline-offset:2px}.input-label{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.input-slim{padding-top:.5rem;padding-bottom:.5rem}.form-checkbox{cursor:pointer;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.form-select{border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.alert{margin-top:.5rem;margin-bottom:.25rem;border-left-width:2px;--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity));padding:.75rem 1rem;font-size:.875rem;line-height:1.25rem}.alert-success{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity))}.alert-failure{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .625rem;font-size:.75rem;font-weight:500;line-height:1rem}.badge-light{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.badge-primary{--tw-bg-opacity: 1;background-color:rgb(191 219 254 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.badge-danger{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.badge-success{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(16 185 129 / var(--tw-text-opacity))}.badge-secondary{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.badge-warning{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity))}.badge-info{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}@media (min-width: 640px){.dataTables_length{margin-top:1.25rem!important;margin-bottom:1.25rem!important}}@media (min-width: 1024px){.dataTables_length{margin-top:1rem!important;margin-bottom:1rem!important}}.dataTables_length select{margin-left:.5rem!important;margin-right:.5rem!important;--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity))!important;margin-top:.5rem;align-items:center;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.dataTables_length select:focus{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity));outline:2px solid transparent;outline-offset:2px}.dataTables_filter{margin-bottom:1rem}.dataTables_filter input{margin-top:.5rem;align-items:center;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.dataTables_filter input:focus{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity));outline:2px solid transparent;outline-offset:2px}@media (min-width: 1024px){.dataTables_filter{margin-top:-3rem!important}}.dataTables_paginate{padding-bottom:1.5rem!important;padding-top:.5rem!important}.dataTables_paginate .paginate_button{margin-right:.25rem!important;cursor:pointer!important;border-width:1px!important;--tw-border-opacity: 1 !important;border-color:rgb(209 213 219 / var(--tw-border-opacity))!important;--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity))!important;font-weight:500!important;--tw-text-opacity: 1 !important;color:rgb(55 65 81 / var(--tw-text-opacity))!important;border-radius:.25rem;padding:.75rem 1rem;font-size:.875rem;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dataTables_paginate .current{--tw-bg-opacity: 1 !important;background-color:rgb(37 99 235 / var(--tw-bg-opacity))!important;--tw-text-opacity: 1 !important;color:rgb(255 255 255 / var(--tw-text-opacity))!important}.dataTables_info{font-size:.875rem!important;line-height:1.25rem!important}.dataTables_empty{padding-top:1rem!important;padding-bottom:1rem!important}.pagination{display:flex!important;align-items:center!important}.pagination .page-link{margin-top:-1px!important;display:inline-flex!important;cursor:pointer!important;align-items:center!important;border-top-width:2px!important;border-color:transparent!important;padding-left:1rem!important;padding-right:1rem!important;padding-top:1rem!important;font-size:.875rem!important;font-weight:500!important;line-height:1.25rem!important;--tw-text-opacity: 1 !important;color:rgb(107 114 128 / var(--tw-text-opacity))!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter!important;transition-duration:.15s!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important}.pagination .page-link:hover{--tw-border-opacity: 1 !important;border-color:rgb(209 213 219 / var(--tw-border-opacity))!important;--tw-text-opacity: 1 !important;color:rgb(55 65 81 / var(--tw-text-opacity))!important}.pagination .page-link:focus{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity));--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity));outline:2px solid transparent;outline-offset:2px}.pagination .active>span{--tw-border-opacity: 1 !important;border-color:rgb(37 99 235 / var(--tw-border-opacity))!important;--tw-text-opacity: 1 !important;color:rgb(37 99 235 / var(--tw-text-opacity))!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.left-0{left:0}.left-1{left:.25rem}.right-0{right:0}.top-0{top:0}.top-1{top:.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-auto{grid-column:auto}.col-span-1{grid-column:span 1 / span 1}.col-span-12{grid-column:span 12 / span 12}.col-span-2{grid-column:span 2 / span 2}.col-span-3{grid-column:span 3 / span 3}.col-span-4{grid-column:span 4 / span 4}.col-span-6{grid-column:span 6 / span 6}.col-span-8{grid-column:span 8 / span 8}.float-end{float:inline-end}.float-right{float:right}.m-0{margin:0}.m-auto{margin:auto}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.-my-6{margin-top:-1.5rem;margin-bottom:-1.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.mx-\[22px\]{margin-left:22px;margin-right:22px}.mx-\[40px\]{margin-left:40px;margin-right:40px}.mx-\[auto\],.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-ml-1{margin-left:-.25rem}.-ml-4{margin-left:-1rem}.-ml-px{margin-left:-1px}.-mr-1{margin-right:-.25rem}.-mr-14{margin-right:-3.5rem}.-mt-4{margin-top:-1rem}.-mt-6{margin-top:-1.5rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-10{margin-bottom:2.5rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb-\[10px\]{margin-bottom:10px}.mb-\[11px\]{margin-bottom:11px}.mb-\[20px\]{margin-bottom:20px}.mb-\[25px\]{margin-bottom:25px}.mb-\[26px\]{margin-bottom:26px}.mb-\[36px\]{margin-bottom:36px}.mb-\[40px\]{margin-bottom:40px}.mb-\[5px\]{margin-bottom:5px}.ml-0{margin-left:0}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.ml-\[10px\]{margin-left:10px}.mr-0{margin-right:0}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-5{margin-right:1.25rem}.mr-6{margin-right:1.5rem}.mt-0{margin-top:0}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-20{margin-top:5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-\[30px\]{margin-top:30px}.mt-\[50px\]{margin-top:50px}.mt-\[auto\]{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-0{height:0px}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-24{height:6rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-\[1px\]{height:1px}.h-\[40px\]{height:40px}.h-auto{height:auto}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.h-screen{height:100vh}.min-h-\[450px\]{min-height:450px}.min-h-screen{min-height:100vh}.w-0{width:0px}.w-1{width:.25rem}.w-1\/2{width:50%}.w-1\/4{width:25%}.w-1\/6{width:16.666667%}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-4\/5{width:80%}.w-4\/6{width:66.666667%}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\/6{width:83.333333%}.w-56{width:14rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[100\%\]{width:100%}.w-\[87px\]{width:87px}.w-auto{width:auto}.w-full{width:100%}.w-screen{width:100vw}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-\[212px\]{max-width:212px}.max-w-\[450px\]{max-width:450px}.max-w-\[625px\]{max-width:625px}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-1\/2{flex-basis:50%}.basis-full{flex-basis:100%}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-top-right{transform-origin:top right}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-4{--tw-translate-y: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.place-content-end{place-content:end}.place-items-center{place-items:center}.content-center{align-content:center}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.gap-0\.5{gap:.125rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.gap-\[13px\]{gap:13px}.gap-\[44px\]{gap:44px}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-\[20px\]{row-gap:20px}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(0px * var(--tw-space-x-reverse));margin-left:calc(0px * calc(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overflow-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[10px\]{border-radius:10px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-sm{border-radius:.125rem}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-2{border-top-width:2px}.border-t-4{border-top-width:4px}.border-t-\[0px\]{border-top-width:0px}.border-t-\[10px\]{border-top-width:10px}.border-t-\[1px\]{border-top-width:1px}.border-solid{border-style:solid}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-\[\#E5E7EB\]{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity))}.border-emerald-500{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity))}.border-fuchsia-600{--tw-border-opacity: 1;border-color:rgb(192 38 211 / var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity: 1;border-color:rgb(107 114 128 / var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.border-red-300{--tw-border-opacity: 1;border-color:rgb(252 165 165 / var(--tw-border-opacity))}.border-red-400{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.border-red-900{--tw-border-opacity: 1;border-color:rgb(127 29 29 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-opacity-50{--tw-border-opacity: .5}.bg-\[\#F2F9FE\]{--tw-bg-opacity: 1;background-color:rgb(242 249 254 / var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity))}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-opacity-100{--tw-bg-opacity: 1}.bg-clip-padding{background-clip:padding-box}.fill-current{fill:currentColor}.object-cover{-o-object-fit:cover;object-fit:cover}.object-scale-down{-o-object-fit:scale-down;object-fit:scale-down}.object-center{-o-object-position:center;object-position:center}.p-1{padding:.25rem}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.p-\[12px\]{padding:12px}.p-\[20px\]{padding:20px}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-12{padding-left:3rem;padding-right:3rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-\[12px\]{padding-left:12px;padding-right:12px}.px-\[20px\]{padding-left:20px;padding-right:20px}.px-\[22px\]{padding-left:22px;padding-right:22px}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-\[33px\]{padding-top:33px;padding-bottom:33px}.py-\[36px\]{padding-top:36px;padding-bottom:36px}.py-\[9\.5px\]{padding-top:9.5px;padding-bottom:9.5px}.pb-10{padding-bottom:2.5rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pb-\[20px\]{padding-bottom:20px}.pb-\[56px\]{padding-bottom:56px}.pb-\[58px\]{padding-bottom:58px}.pl-0{padding-left:0}.pl-1\.5{padding-left:.375rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-\[18px\]{padding-left:18px}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pr-4{padding-right:1rem}.pr-\[18px\]{padding-right:18px}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pt-\[20px\]{padding-top:20px}.pt-\[29px\]{padding-top:29px}.pt-\[35px\]{padding-top:35px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-\[\'Open_Sans\'\]{font-family:Open Sans}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-5xl{font-size:3rem;line-height:1}.text-\[12px\]{font-size:12px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[22px\]{font-size:22px}.text-\[24px\]{font-size:24px}.text-\[35px\]{font-size:35px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-\[16px\]{font-weight:16px}.font-bold{font-weight:700}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-9{line-height:2.25rem}.leading-\[1\.2rem\]{line-height:1.2rem}.leading-\[1\.35em\]{line-height:1.35em}.leading-\[1\.36em\]{line-height:1.36em}.leading-\[1\.375em\]{line-height:1.375em}.leading-\[1\.3em\]{line-height:1.3em}.leading-\[1\.5em\]{line-height:1.5em}.leading-\[1\.75em\]{line-height:1.75em}.leading-normal{line-height:1.5}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#212529\]{--tw-text-opacity: 1;color:rgb(33 37 41 / var(--tw-text-opacity))}.text-\[\#6C727F\]{--tw-text-opacity: 1;color:rgb(108 114 127 / var(--tw-text-opacity))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-red-900{--tw-text-opacity: 1;color:rgb(127 29 29 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-black{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity: .05}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-1000{transition-duration:1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-75{transition-duration:75ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-linear{transition-timing-function:linear}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.placeholder\:text-gray-500::placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:left-\[8px\]:after{content:var(--tw-content);left:8px}.after\:top-\[5px\]:after{content:var(--tw-content);top:5px}.after\:h-\[30px\]:after{content:var(--tw-content);height:30px}.after\:w-\[30px\]:after{content:var(--tw-content);width:30px}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content: "";content:var(--tw-content)}.focus-within\:z-10:focus-within{z-index:10}.hover\:list-disc:hover{list-style-type:disc}.hover\:border-blue-600:hover{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity))}.hover\:border-gray-600:hover{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.hover\:border-gray-800:hover{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:transparent}.hover\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.hover\:bg-red-900:hover{--tw-bg-opacity: 1;background-color:rgb(127 29 29 / var(--tw-bg-opacity))}.hover\:font-semibold:hover{font-weight:600}.hover\:text-blue-600:hover{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity: 1;color:rgb(49 46 129 / var(--tw-text-opacity))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-2xl:hover{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:border-blue-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity))}.focus\:border-fuchsia-300:focus{--tw-border-opacity: 1;border-color:rgb(240 171 252 / var(--tw-border-opacity))}.focus\:border-indigo-500:focus{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity))}.focus\:border-red-500:focus{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.focus\:bg-gray-600:focus{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}.focus\:bg-white:focus{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.focus\:font-semibold:focus{font-weight:600}.focus\:text-gray-500:focus{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.focus\:text-gray-600:focus{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.focus\:text-gray-900:focus{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.focus\:underline:focus{text-decoration-line:underline}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(107 114 128 / var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.active\:bg-gray-50:active{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.active\:text-gray-800:active{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.active\:outline-none:active{outline:2px solid transparent;outline-offset:2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-50:disabled{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-75:disabled{opacity:.75}.group:hover .group-hover\:border-transparent{border-color:transparent}.group:hover .group-hover\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.group:hover .group-hover\:opacity-100{opacity:1}.peer:checked~.peer-checked\:text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.peer:checked~.peer-checked\:after\:translate-x-\[140\%\]:after{content:var(--tw-content);--tw-translate-x: 140%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.peer:focus~.peer-focus\:outline-none{outline:2px solid transparent;outline-offset:2px}@media (min-width: 640px){.sm\:inset-0{top:0;right:0;bottom:0;left:0}.sm\:col-span-2{grid-column:span 2 / span 2}.sm\:col-span-3{grid-column:span 3 / span 3}.sm\:col-span-4{grid-column:span 4 / span 4}.sm\:col-span-6{grid-column:span 6 / span 6}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:ml-3{margin-left:.75rem}.sm\:ml-4{margin-left:1rem}.sm\:ml-6{margin-left:1.5rem}.sm\:mt-0{margin-top:0}.sm\:mt-4{margin-top:1rem}.sm\:mt-6{margin-top:1.5rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:hidden{display:none}.sm\:h-10{height:2.5rem}.sm\:h-screen{height:100vh}.sm\:w-10{width:2.5rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-sm{max-width:24rem}.sm\:flex-shrink-0{flex-shrink:0}.sm\:translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:flex-nowrap{flex-wrap:nowrap}.sm\:items-start{align-items:flex-start}.sm\:items-center{align-items:center}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:1rem}.sm\:rounded-lg{border-radius:.5rem}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:text-left{text-align:left}.sm\:align-middle{vertical-align:middle}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 768px){.md\:col-span-1{grid-column:span 1 / span 1}.md\:col-span-2{grid-column:span 2 / span 2}.md\:col-span-4{grid-column:span 4 / span 4}.md\:col-span-5{grid-column:span 5 / span 5}.md\:col-span-6{grid-column:span 6 / span 6}.md\:col-start-2{grid-column-start:2}.md\:col-start-4{grid-column-start:4}.md\:mx-0,.md\:mx-\[0\]{margin-left:0;margin-right:0}.md\:-mr-1{margin-right:-.25rem}.md\:mb-6{margin-bottom:1.5rem}.md\:mb-\[46px\]{margin-bottom:46px}.md\:ml-2{margin-left:.5rem}.md\:ml-6{margin-left:1.5rem}.md\:mr-0{margin-right:0}.md\:mr-2{margin-right:.5rem}.md\:mt-0{margin-top:0}.md\:mt-10{margin-top:2.5rem}.md\:mt-5{margin-top:1.25rem}.md\:block{display:block}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:min-h-\[411px\]{min-height:411px}.md\:w-1\/2{width:50%}.md\:w-1\/3{width:33.333333%}.md\:max-w-3xl{max-width:48rem}.md\:max-w-xl{max-width:36rem}.md\:flex-shrink-0{flex-shrink:0}.md\:shrink{flex-shrink:1}.md\:grow-0{flex-grow:0}.md\:basis-1\/2{flex-basis:50%}.md\:basis-\[449px\]{flex-basis:449px}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:items-center{align-items:center}.md\:justify-center{justify-content:center}.md\:justify-between{justify-content:space-between}.md\:gap-6{gap:1.5rem}.md\:gap-x-\[21px\]{-moz-column-gap:21px;column-gap:21px}.md\:gap-y-6{row-gap:1.5rem}.md\:border-r{border-right-width:1px}.md\:border-\[\#E5E7EB\]{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.md\:p-24{padding:6rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:px-\[40px\]{padding-left:40px;padding-right:40px}.md\:pb-\[40px\]{padding-bottom:40px}.md\:pl-4{padding-left:1rem}.md\:pl-\[52px\]{padding-left:52px}.md\:pl-\[61px\]{padding-left:61px}.md\:pr-\[20px\]{padding-right:20px}.md\:pr-\[48px\]{padding-right:48px}.md\:pt-0{padding-top:0}.md\:pt-\[58px\]{padding-top:58px}.md\:text-left{text-align:left}.md\:text-center{text-align:center}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-\[30px\]{font-size:30px}.md\:text-\[32px\]{font-size:32px}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 1024px){.lg\:col-span-3{grid-column:span 3 / span 3}.lg\:col-span-6{grid-column:span 6 / span 6}.lg\:col-span-7{grid-column:span 7 / span 7}.lg\:col-span-8{grid-column:span 8 / span 8}.lg\:col-start-3{grid-column-start:3}.lg\:col-start-4{grid-column-start:4}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:-mb-1{margin-bottom:-.25rem}.lg\:-ml-5{margin-left:-1.25rem}.lg\:mt-24{margin-top:6rem}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:h-screen{height:100vh}.lg\:w-1\/2{width:50%}.lg\:w-1\/3{width:33.333333%}.lg\:w-1\/4{width:25%}.lg\:w-1\/5{width:20%}.lg\:max-w-\[80\%\]{max-width:80%}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:items-center{align-items:center}.lg\:gap-4{gap:1rem}.lg\:rounded-lg{border-radius:.5rem}.lg\:px-16{padding-left:4rem;padding-right:4rem}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:px-4{padding-left:1rem;padding-right:1rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-2{padding-top:.5rem;padding-bottom:.5rem}}@media (min-width: 1280px){.xl\:col-span-4{grid-column:span 4 / span 4}.xl\:col-span-6{grid-column:span 6 / span 6}.xl\:col-span-8{grid-column:span 8 / span 8}.xl\:col-span-9{grid-column:span 9 / span 9}.xl\:col-start-4{grid-column-start:4}.xl\:ml-5{margin-left:1.25rem}.xl\:mt-0{margin-top:0}.xl\:mt-32{margin-top:8rem}.xl\:flex{display:flex}.xl\:w-auto{width:auto}.xl\:basis-auto{flex-basis:auto}.xl\:flex-row{flex-direction:row}.xl\:flex-nowrap{flex-wrap:nowrap}.xl\:justify-center{justify-content:center}.xl\:border-r{border-right-width:1px}.xl\:border-\[\#E5E7EB\]{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.xl\:px-16{padding-left:4rem;padding-right:4rem}.xl\:px-20{padding-left:5rem;padding-right:5rem}.xl\:px-5{padding-left:1.25rem;padding-right:1.25rem}.xl\:pr-20{padding-right:5rem}}@media (prefers-color-scheme: dark){.dark\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.dark\:bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(156 163 175 / var(--tw-placeholder-opacity))}.dark\:placeholder-gray-400::placeholder{--tw-placeholder-opacity: 1;color:rgb(156 163 175 / var(--tw-placeholder-opacity))}.dark\:focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity))}.dark\:focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity))}} +*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Open Sans,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}[type=text],input:where(:not([type])),[type=email],[type=url],[type=password],[type=number],[type=date],[type=datetime-local],[type=month],[type=search],[type=tel],[type=time],[type=week],[multiple],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}[type=text]:focus,input:where(:not([type])):focus,[type=email]:focus,[type=url]:focus,[type=password]:focus,[type=number]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=month]:focus,[type=search]:focus,[type=tel]:focus,[type=time]:focus,[type=week]:focus,[multiple]:focus,textarea:focus,select:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media (forced-colors: active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:hover,[type=checkbox]:checked:focus,[type=radio]:checked:hover,[type=radio]:checked:focus{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media (forced-colors: active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:hover,[type=checkbox]:indeterminate:focus{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.form-input,.form-textarea,.form-select,.form-multiselect{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow: 0 0 #0000}.form-input:focus,.form-textarea:focus,.form-select:focus,.form-multiselect:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}.form-select{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}.form-select:where([size]:not([size="1"])){background-image:initial;background-position:initial;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}.form-checkbox,.form-radio{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow: 0 0 #0000}.form-checkbox{border-radius:0}.form-radio{border-radius:100%}.form-checkbox:focus,.form-radio:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 2px;--tw-ring-offset-color: #fff;--tw-ring-color: #2563eb;--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.form-checkbox:checked,.form-radio:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}.form-checkbox:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")}@media (forced-colors: active){.form-checkbox:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}.form-radio:checked{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e")}@media (forced-colors: active){.form-radio:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}.form-checkbox:checked:hover,.form-checkbox:checked:focus,.form-radio:checked:hover,.form-radio:checked:focus{border-color:transparent;background-color:currentColor}.form-checkbox:indeterminate{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:center;background-repeat:no-repeat}@media (forced-colors: active){.form-checkbox:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}.form-checkbox:indeterminate:hover,.form-checkbox:indeterminate:focus{border-color:transparent;background-color:currentColor}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"“""”""‘""’";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows) / 10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:transparent;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body: #374151;--tw-prose-headings: #111827;--tw-prose-lead: #4b5563;--tw-prose-links: #111827;--tw-prose-bold: #111827;--tw-prose-counters: #6b7280;--tw-prose-bullets: #d1d5db;--tw-prose-hr: #e5e7eb;--tw-prose-quotes: #111827;--tw-prose-quote-borders: #e5e7eb;--tw-prose-captions: #6b7280;--tw-prose-kbd: #111827;--tw-prose-kbd-shadows: 17 24 39;--tw-prose-code: #111827;--tw-prose-pre-code: #e5e7eb;--tw-prose-pre-bg: #1f2937;--tw-prose-th-borders: #d1d5db;--tw-prose-td-borders: #e5e7eb;--tw-prose-invert-body: #d1d5db;--tw-prose-invert-headings: #fff;--tw-prose-invert-lead: #9ca3af;--tw-prose-invert-links: #fff;--tw-prose-invert-bold: #fff;--tw-prose-invert-counters: #9ca3af;--tw-prose-invert-bullets: #4b5563;--tw-prose-invert-hr: #374151;--tw-prose-invert-quotes: #f3f4f6;--tw-prose-invert-quote-borders: #374151;--tw-prose-invert-captions: #9ca3af;--tw-prose-invert-kbd: #fff;--tw-prose-invert-kbd-shadows: 255 255 255;--tw-prose-invert-code: #fff;--tw-prose-invert-pre-code: #d1d5db;--tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);--tw-prose-invert-th-borders: #4b5563;--tw-prose-invert-td-borders: #374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.button{border-radius:.25rem;padding:.75rem 1rem;font-size:.875rem;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}button:disabled{cursor:not-allowed;opacity:.5}.button-primary{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.button-primary:hover{font-weight:600}.button-block{display:block;width:100%}.button-danger{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.button-danger:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity))}.button-secondary{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.button-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.button-link{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.button-link:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity));text-decoration-line:underline}.button-link:focus{text-decoration-line:underline;outline:2px solid transparent;outline-offset:2px}.validation{margin-top:.5rem;margin-bottom:.25rem;border-left-width:2px;--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity));padding:.25rem .75rem}.validation-fail{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.validation-pass{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.input{margin-top:.5rem;align-items:center;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.input:focus{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity));outline:2px solid transparent;outline-offset:2px}.input-label{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.input-slim{padding-top:.5rem;padding-bottom:.5rem}.form-checkbox{cursor:pointer;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.form-select{border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.alert{margin-top:.5rem;margin-bottom:.25rem;border-left-width:2px;--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity));padding:.75rem 1rem;font-size:.875rem;line-height:1.25rem}.alert-success{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity))}.alert-failure{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.badge{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .625rem;font-size:.75rem;font-weight:500;line-height:1rem}.badge-light{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.badge-primary{--tw-bg-opacity: 1;background-color:rgb(191 219 254 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.badge-danger{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.badge-success{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(16 185 129 / var(--tw-text-opacity))}.badge-secondary{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.badge-warning{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity))}.badge-info{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}@media (min-width: 640px){.dataTables_length{margin-top:1.25rem!important;margin-bottom:1.25rem!important}}@media (min-width: 1024px){.dataTables_length{margin-top:1rem!important;margin-bottom:1rem!important}}.dataTables_length select{margin-left:.5rem!important;margin-right:.5rem!important;--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity))!important;margin-top:.5rem;align-items:center;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.dataTables_length select:focus{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity));outline:2px solid transparent;outline-offset:2px}.dataTables_filter{margin-bottom:1rem}.dataTables_filter input{margin-top:.5rem;align-items:center;border-radius:.25rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity));padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.dataTables_filter input:focus{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity));outline:2px solid transparent;outline-offset:2px}@media (min-width: 1024px){.dataTables_filter{margin-top:-3rem!important}}.dataTables_paginate{padding-bottom:1.5rem!important;padding-top:.5rem!important}.dataTables_paginate .paginate_button{margin-right:.25rem!important;cursor:pointer!important;border-width:1px!important;--tw-border-opacity: 1 !important;border-color:rgb(209 213 219 / var(--tw-border-opacity))!important;--tw-bg-opacity: 1 !important;background-color:rgb(255 255 255 / var(--tw-bg-opacity))!important;font-weight:500!important;--tw-text-opacity: 1 !important;color:rgb(55 65 81 / var(--tw-text-opacity))!important;border-radius:.25rem;padding:.75rem 1rem;font-size:.875rem;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dataTables_paginate .current{--tw-bg-opacity: 1 !important;background-color:rgb(37 99 235 / var(--tw-bg-opacity))!important;--tw-text-opacity: 1 !important;color:rgb(255 255 255 / var(--tw-text-opacity))!important}.dataTables_info{font-size:.875rem!important;line-height:1.25rem!important}.dataTables_empty{padding-top:1rem!important;padding-bottom:1rem!important}.pagination{display:flex!important;align-items:center!important}.pagination .page-link{margin-top:-1px!important;display:inline-flex!important;cursor:pointer!important;align-items:center!important;border-top-width:2px!important;border-color:transparent!important;padding-left:1rem!important;padding-right:1rem!important;padding-top:1rem!important;font-size:.875rem!important;font-weight:500!important;line-height:1.25rem!important;--tw-text-opacity: 1 !important;color:rgb(107 114 128 / var(--tw-text-opacity))!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter!important;transition-duration:.15s!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important}.pagination .page-link:hover{--tw-border-opacity: 1 !important;border-color:rgb(209 213 219 / var(--tw-border-opacity))!important;--tw-text-opacity: 1 !important;color:rgb(55 65 81 / var(--tw-text-opacity))!important}.pagination .page-link:focus{--tw-border-opacity: 1;border-color:rgb(156 163 175 / var(--tw-border-opacity));--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity));outline:2px solid transparent;outline-offset:2px}.pagination .active>span{--tw-border-opacity: 1 !important;border-color:rgb(37 99 235 / var(--tw-border-opacity))!important;--tw-text-opacity: 1 !important;color:rgb(37 99 235 / var(--tw-text-opacity))!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.left-0{left:0}.left-1{left:.25rem}.right-0{right:0}.top-0{top:0}.top-1{top:.25rem}.z-0{z-index:0}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.col-auto{grid-column:auto}.col-span-1{grid-column:span 1 / span 1}.col-span-12{grid-column:span 12 / span 12}.col-span-2{grid-column:span 2 / span 2}.col-span-3{grid-column:span 3 / span 3}.col-span-4{grid-column:span 4 / span 4}.col-span-6{grid-column:span 6 / span 6}.col-span-8{grid-column:span 8 / span 8}.float-end{float:inline-end}.float-right{float:right}.m-0{margin:0}.m-auto{margin:auto}.-my-2{margin-top:-.5rem;margin-bottom:-.5rem}.-my-6{margin-top:-1.5rem;margin-bottom:-1.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.mx-\[22px\]{margin-left:22px;margin-right:22px}.mx-\[40px\]{margin-left:40px;margin-right:40px}.mx-\[auto\],.mx-auto{margin-left:auto;margin-right:auto}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-ml-1{margin-left:-.25rem}.-ml-4{margin-left:-1rem}.-ml-px{margin-left:-1px}.-mr-1{margin-right:-.25rem}.-mr-14{margin-right:-3.5rem}.-mt-4{margin-top:-1rem}.-mt-6{margin-top:-1.5rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-10{margin-bottom:2.5rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb-\[10px\]{margin-bottom:10px}.mb-\[11px\]{margin-bottom:11px}.mb-\[20px\]{margin-bottom:20px}.mb-\[25px\]{margin-bottom:25px}.mb-\[26px\]{margin-bottom:26px}.mb-\[36px\]{margin-bottom:36px}.mb-\[40px\]{margin-bottom:40px}.mb-\[5px\]{margin-bottom:5px}.ml-0{margin-left:0}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-5{margin-left:1.25rem}.ml-\[10px\]{margin-left:10px}.mr-0{margin-right:0}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mr-5{margin-right:1.25rem}.mr-6{margin-right:1.5rem}.mt-0{margin-top:0}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-20{margin-top:5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-\[30px\]{margin-top:30px}.mt-\[50px\]{margin-top:50px}.mt-\[auto\]{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-0{height:0px}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-24{height:6rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-\[1px\]{height:1px}.h-\[40px\]{height:40px}.h-auto{height:auto}.h-fit{height:-moz-fit-content;height:fit-content}.h-full{height:100%}.h-screen{height:100vh}.min-h-\[450px\]{min-height:450px}.min-h-screen{min-height:100vh}.w-0{width:0px}.w-1{width:.25rem}.w-1\/2{width:50%}.w-1\/4{width:25%}.w-1\/6{width:16.666667%}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-4\/5{width:80%}.w-4\/6{width:66.666667%}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\/6{width:83.333333%}.w-56{width:14rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[100\%\]{width:100%}.w-\[87px\]{width:87px}.w-auto{width:auto}.w-full{width:100%}.w-screen{width:100vw}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-\[212px\]{max-width:212px}.max-w-\[450px\]{max-width:450px}.max-w-\[625px\]{max-width:625px}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-1\/2{flex-basis:50%}.basis-full{flex-basis:100%}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-top-right{transform-origin:top right}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-4{--tw-translate-y: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.place-content-end{place-content:end}.place-items-center{place-items:center}.content-center{align-content:center}.content-start{align-content:flex-start}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-stretch{justify-content:stretch}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.gap-\[13px\]{gap:13px}.gap-\[44px\]{gap:44px}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-\[20px\]{row-gap:20px}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(0px * var(--tw-space-x-reverse));margin-left:calc(0px * calc(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.75rem * var(--tw-space-x-reverse));margin-left:calc(.75rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2.5rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overflow-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[10px\]{border-radius:10px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-sm{border-radius:.125rem}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-2{border-top-width:2px}.border-t-4{border-top-width:4px}.border-t-\[0px\]{border-top-width:0px}.border-t-\[10px\]{border-top-width:10px}.border-t-\[1px\]{border-top-width:1px}.border-solid{border-style:solid}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-\[\#E5E7EB\]{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity))}.border-emerald-500{--tw-border-opacity: 1;border-color:rgb(16 185 129 / var(--tw-border-opacity))}.border-fuchsia-600{--tw-border-opacity: 1;border-color:rgb(192 38 211 / var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity: 1;border-color:rgb(107 114 128 / var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.border-red-300{--tw-border-opacity: 1;border-color:rgb(252 165 165 / var(--tw-border-opacity))}.border-red-400{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity))}.border-red-500{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.border-red-900{--tw-border-opacity: 1;border-color:rgb(127 29 29 / var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-opacity-50{--tw-border-opacity: .5}.bg-\[\#F2F9FE\]{--tw-bg-opacity: 1;background-color:rgb(242 249 254 / var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.bg-blue-700{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity))}.bg-emerald-600{--tw-bg-opacity: 1;background-color:rgb(5 150 105 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity: 1;background-color:rgb(107 114 128 / var(--tw-bg-opacity))}.bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-opacity-100{--tw-bg-opacity: 1}.bg-clip-padding{background-clip:padding-box}.fill-current{fill:currentColor}.object-cover{-o-object-fit:cover;object-fit:cover}.object-scale-down{-o-object-fit:scale-down;object-fit:scale-down}.object-center{-o-object-position:center;object-position:center}.p-1{padding:.25rem}.p-10{padding:2.5rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.p-\[12px\]{padding:12px}.p-\[20px\]{padding:20px}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-12{padding-left:3rem;padding-right:3rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-\[12px\]{padding-left:12px;padding-right:12px}.px-\[20px\]{padding-left:20px;padding-right:20px}.px-\[22px\]{padding-left:22px;padding-right:22px}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-\[33px\]{padding-top:33px;padding-bottom:33px}.py-\[36px\]{padding-top:36px;padding-bottom:36px}.py-\[9\.5px\]{padding-top:9.5px;padding-bottom:9.5px}.pb-10{padding-bottom:2.5rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pb-\[20px\]{padding-bottom:20px}.pb-\[56px\]{padding-bottom:56px}.pb-\[58px\]{padding-bottom:58px}.pl-0{padding-left:0}.pl-1\.5{padding-left:.375rem}.pl-2{padding-left:.5rem}.pl-3{padding-left:.75rem}.pl-\[18px\]{padding-left:18px}.pr-10{padding-right:2.5rem}.pr-2{padding-right:.5rem}.pr-4{padding-right:1rem}.pr-\[18px\]{padding-right:18px}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pt-\[20px\]{padding-top:20px}.pt-\[29px\]{padding-top:29px}.pt-\[35px\]{padding-top:35px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-\[\'Open_Sans\'\]{font-family:Open Sans}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-5xl{font-size:3rem;line-height:1}.text-\[12px\]{font-size:12px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[22px\]{font-size:22px}.text-\[24px\]{font-size:24px}.text-\[35px\]{font-size:35px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-\[16px\]{font-weight:16px}.font-bold{font-weight:700}.font-light{font-weight:300}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-9{line-height:2.25rem}.leading-\[1\.2rem\]{line-height:1.2rem}.leading-\[1\.35em\]{line-height:1.35em}.leading-\[1\.36em\]{line-height:1.36em}.leading-\[1\.375em\]{line-height:1.375em}.leading-\[1\.3em\]{line-height:1.3em}.leading-\[1\.5em\]{line-height:1.5em}.leading-\[1\.75em\]{line-height:1.75em}.leading-normal{line-height:1.5}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#212529\]{--tw-text-opacity: 1;color:rgb(33 37 41 / var(--tw-text-opacity))}.text-\[\#6C727F\]{--tw-text-opacity: 1;color:rgb(108 114 127 / var(--tw-text-opacity))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity))}.text-emerald-600{--tw-text-opacity: 1;color:rgb(5 150 105 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity))}.text-red-900{--tw-text-opacity: 1;color:rgb(127 29 29 / var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-black{--tw-ring-opacity: 1;--tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity))}.ring-opacity-5{--tw-ring-opacity: .05}.grayscale{--tw-grayscale: grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-1000{transition-duration:1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-75{transition-duration:75ms}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-linear{transition-timing-function:linear}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.placeholder\:text-gray-500::-moz-placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.placeholder\:text-gray-500::placeholder{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:left-\[8px\]:after{content:var(--tw-content);left:8px}.after\:top-\[5px\]:after{content:var(--tw-content);top:5px}.after\:h-\[30px\]:after{content:var(--tw-content);height:30px}.after\:w-\[30px\]:after{content:var(--tw-content);width:30px}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content: "";content:var(--tw-content)}.focus-within\:z-10:focus-within{z-index:10}.hover\:list-disc:hover{list-style-type:disc}.hover\:border-blue-600:hover{--tw-border-opacity: 1;border-color:rgb(37 99 235 / var(--tw-border-opacity))}.hover\:border-gray-600:hover{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.hover\:border-gray-800:hover{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:transparent}.hover\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity))}.hover\:bg-blue-600:hover{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity))}.hover\:bg-red-900:hover{--tw-bg-opacity: 1;background-color:rgb(127 29 29 / var(--tw-bg-opacity))}.hover\:font-semibold:hover{font-weight:600}.hover\:text-blue-600:hover{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.hover\:text-gray-800:hover{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.hover\:text-indigo-900:hover{--tw-text-opacity: 1;color:rgb(49 46 129 / var(--tw-text-opacity))}.hover\:text-red-500:hover{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-2xl:hover{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.hover\:shadow-sm:hover{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:border-blue-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity))}.focus\:border-fuchsia-300:focus{--tw-border-opacity: 1;border-color:rgb(240 171 252 / var(--tw-border-opacity))}.focus\:border-indigo-500:focus{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity))}.focus\:border-red-500:focus{--tw-border-opacity: 1;border-color:rgb(239 68 68 / var(--tw-border-opacity))}.focus\:bg-gray-100:focus{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.focus\:bg-gray-600:focus{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity))}.focus\:bg-white:focus{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.focus\:font-semibold:focus{font-weight:600}.focus\:text-gray-500:focus{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.focus\:text-gray-600:focus{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.focus\:text-gray-900:focus{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.focus\:underline:focus{text-decoration-line:underline}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity))}.focus\:ring-gray-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(107 114 128 / var(--tw-ring-opacity))}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity))}.focus\:ring-indigo-600:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity))}.focus\:ring-red-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity))}.focus\:ring-opacity-50:focus{--tw-ring-opacity: .5}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.active\:bg-gray-50:active{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.active\:text-gray-800:active{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity))}.active\:outline-none:active{outline:2px solid transparent;outline-offset:2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-50:disabled{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity))}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-75:disabled{opacity:.75}.group:hover .group-hover\:border-transparent{border-color:transparent}.group:hover .group-hover\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.group:hover .group-hover\:opacity-100{opacity:1}.peer:checked~.peer-checked\:text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity))}.peer:checked~.peer-checked\:after\:translate-x-\[140\%\]:after{content:var(--tw-content);--tw-translate-x: 140%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.peer:focus~.peer-focus\:outline-none{outline:2px solid transparent;outline-offset:2px}@media (min-width: 640px){.sm\:inset-0{top:0;right:0;bottom:0;left:0}.sm\:col-span-2{grid-column:span 2 / span 2}.sm\:col-span-3{grid-column:span 3 / span 3}.sm\:col-span-4{grid-column:span 4 / span 4}.sm\:col-span-6{grid-column:span 6 / span 6}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mx-0{margin-left:0;margin-right:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:ml-3{margin-left:.75rem}.sm\:ml-4{margin-left:1rem}.sm\:ml-6{margin-left:1.5rem}.sm\:mt-0{margin-top:0}.sm\:mt-4{margin-top:1rem}.sm\:mt-6{margin-top:1.5rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:hidden{display:none}.sm\:h-10{height:2.5rem}.sm\:h-screen{height:100vh}.sm\:w-10{width:2.5rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:max-w-lg{max-width:32rem}.sm\:max-w-sm{max-width:24rem}.sm\:flex-shrink-0{flex-shrink:0}.sm\:translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:flex-nowrap{flex-wrap:nowrap}.sm\:items-start{align-items:flex-start}.sm\:items-center{align-items:center}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:1rem}.sm\:rounded-lg{border-radius:.5rem}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:text-left{text-align:left}.sm\:align-middle{vertical-align:middle}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 768px){.md\:col-span-1{grid-column:span 1 / span 1}.md\:col-span-2{grid-column:span 2 / span 2}.md\:col-span-4{grid-column:span 4 / span 4}.md\:col-span-5{grid-column:span 5 / span 5}.md\:col-span-6{grid-column:span 6 / span 6}.md\:col-start-2{grid-column-start:2}.md\:col-start-4{grid-column-start:4}.md\:mx-0,.md\:mx-\[0\]{margin-left:0;margin-right:0}.md\:-mr-1{margin-right:-.25rem}.md\:mb-6{margin-bottom:1.5rem}.md\:mb-\[46px\]{margin-bottom:46px}.md\:ml-2{margin-left:.5rem}.md\:ml-6{margin-left:1.5rem}.md\:mr-0{margin-right:0}.md\:mr-2{margin-right:.5rem}.md\:mt-0{margin-top:0}.md\:mt-10{margin-top:2.5rem}.md\:mt-5{margin-top:1.25rem}.md\:block{display:block}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:min-h-\[411px\]{min-height:411px}.md\:w-1\/2{width:50%}.md\:w-1\/3{width:33.333333%}.md\:max-w-3xl{max-width:48rem}.md\:max-w-xl{max-width:36rem}.md\:flex-shrink-0{flex-shrink:0}.md\:shrink{flex-shrink:1}.md\:grow-0{flex-grow:0}.md\:basis-1\/2{flex-basis:50%}.md\:basis-\[449px\]{flex-basis:449px}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:items-center{align-items:center}.md\:justify-center{justify-content:center}.md\:justify-between{justify-content:space-between}.md\:gap-6{gap:1.5rem}.md\:gap-x-\[21px\]{-moz-column-gap:21px;column-gap:21px}.md\:gap-y-6{row-gap:1.5rem}.md\:border-r{border-right-width:1px}.md\:border-\[\#E5E7EB\]{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.md\:p-24{padding:6rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:px-\[40px\]{padding-left:40px;padding-right:40px}.md\:pb-\[40px\]{padding-bottom:40px}.md\:pl-4{padding-left:1rem}.md\:pl-\[52px\]{padding-left:52px}.md\:pl-\[61px\]{padding-left:61px}.md\:pr-\[20px\]{padding-right:20px}.md\:pr-\[48px\]{padding-right:48px}.md\:pt-0{padding-top:0}.md\:pt-\[58px\]{padding-top:58px}.md\:text-left{text-align:left}.md\:text-center{text-align:center}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-\[30px\]{font-size:30px}.md\:text-\[32px\]{font-size:32px}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 1024px){.lg\:col-span-3{grid-column:span 3 / span 3}.lg\:col-span-6{grid-column:span 6 / span 6}.lg\:col-span-7{grid-column:span 7 / span 7}.lg\:col-span-8{grid-column:span 8 / span 8}.lg\:col-start-3{grid-column-start:3}.lg\:col-start-4{grid-column-start:4}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:-mb-1{margin-bottom:-.25rem}.lg\:-ml-5{margin-left:-1.25rem}.lg\:mt-24{margin-top:6rem}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:h-screen{height:100vh}.lg\:w-1\/2{width:50%}.lg\:w-1\/3{width:33.333333%}.lg\:w-1\/4{width:25%}.lg\:w-1\/5{width:20%}.lg\:max-w-\[80\%\]{max-width:80%}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:items-center{align-items:center}.lg\:gap-4{gap:1rem}.lg\:rounded-lg{border-radius:.5rem}.lg\:px-16{padding-left:4rem;padding-right:4rem}.lg\:px-2{padding-left:.5rem;padding-right:.5rem}.lg\:px-4{padding-left:1rem;padding-right:1rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-2{padding-top:.5rem;padding-bottom:.5rem}}@media (min-width: 1280px){.xl\:col-span-4{grid-column:span 4 / span 4}.xl\:col-span-6{grid-column:span 6 / span 6}.xl\:col-span-8{grid-column:span 8 / span 8}.xl\:col-span-9{grid-column:span 9 / span 9}.xl\:col-start-4{grid-column-start:4}.xl\:ml-5{margin-left:1.25rem}.xl\:mt-0{margin-top:0}.xl\:mt-32{margin-top:8rem}.xl\:flex{display:flex}.xl\:w-auto{width:auto}.xl\:basis-auto{flex-basis:auto}.xl\:flex-row{flex-direction:row}.xl\:flex-nowrap{flex-wrap:nowrap}.xl\:justify-center{justify-content:center}.xl\:border-r{border-right-width:1px}.xl\:border-\[\#E5E7EB\]{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.xl\:px-16{padding-left:4rem;padding-right:4rem}.xl\:px-20{padding-left:5rem;padding-right:5rem}.xl\:px-5{padding-left:1.25rem;padding-right:1.25rem}.xl\:pr-20{padding-right:5rem}}@media (prefers-color-scheme: dark){.dark\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity))}.dark\:bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(156 163 175 / var(--tw-placeholder-opacity))}.dark\:placeholder-gray-400::placeholder{--tw-placeholder-opacity: 1;color:rgb(156 163 175 / var(--tw-placeholder-opacity))}.dark\:focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity))}.dark\:focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity))}} diff --git a/public/build/assets/authorize-authorize-card-39be6d93.js b/public/build/assets/authorize-authorize-card-39be6d93.js new file mode 100644 index 000000000000..f8fddd49dd12 --- /dev/null +++ b/public/build/assets/authorize-authorize-card-39be6d93.js @@ -0,0 +1,9 @@ +/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class o{constructor(e,t){this.publicKey=e,this.loginId=t,this.cardHolderName=document.getElementById("cardholder_name"),this.cardButton=document.getElementById("card_button"),this.sc=createSimpleCard({fields:{card:{number:"#number",date:"#date",cvv:"#cvv"}}}),this.sc.mount()}handleAuthorization(){var r,n,s,c;if(this.cvvRequired=="1"&&document.getElementById("cvv").value.length<3){const d=document.getElementById("errors");d&&(d.innerText="CVV is required",d.style.display="block"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");return}var e={};e.clientKey=this.publicKey,e.apiLoginID=this.loginId;var t={};t.cardNumber=(r=this.sc.value("number"))==null?void 0:r.replace(/[^\d]/g,""),t.month=(n=this.sc.value("month"))==null?void 0:n.replace(/[^\d]/g,""),t.year=`20${(s=this.sc.value("year"))==null?void 0:s.replace(/[^\d]/g,"")}`,t.cardCode=(c=this.sc.value("cvv"))==null?void 0:c.replace(/[^\d]/g,"");var a={};return a.authData=e,a.cardData=t,document.getElementById("card_button").disabled=!0,document.querySelector("#card_button > svg").classList.remove("hidden"),document.querySelector("#card_button > span").classList.add("hidden"),Accept.dispatchData(a,this.responseHandler),!1}responseHandler(e){if(e.messages.resultCode==="Error"){var t=0;const a=document.getElementById("errors");a&&(a.innerText=`${e.messages.message[t].code}: ${e.messages.message[t].text}`,a.style.display="block"),document.getElementById("card_button").disabled=!1,document.querySelector("#card_button > svg").classList.add("hidden"),document.querySelector("#card_button > span").classList.remove("hidden")}else e.messages.resultCode==="Ok"&&(document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue,document.getElementById("server_response").submit());return!1}handle(){return this.cardButton.addEventListener("click",()=>{this.cardButton.disabled=!this.cardButton.disabled,this.handleAuthorization()}),this}}const u=document.querySelector('meta[name="authorize-public-key"]').content,l=document.querySelector('meta[name="authorize-login-id"]').content;document.querySelector('meta[name="authnet-require-cvv"]').content;new o(u,l).handle(); diff --git a/public/build/assets/authorize-authorize-card-7da1185c.js b/public/build/assets/authorize-authorize-card-7da1185c.js deleted file mode 100644 index 677563d9b364..000000000000 --- a/public/build/assets/authorize-authorize-card-7da1185c.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class n{constructor(e,t){this.publicKey=e,this.loginId=t,this.cardHolderName=document.getElementById("cardholder_name"),this.cardButton=document.getElementById("card_button")}handleAuthorization(){if(s=="1"&&document.getElementById("cvv").value.length<3){var e=$("#errors");e.show().html("

CVV is required

"),document.getElementById("card_button").disabled=!1,document.querySelector("#card_button > svg").classList.add("hidden"),document.querySelector("#card_button > span").classList.remove("hidden");return}var t=$("#my-card"),a={};a.clientKey=this.publicKey,a.apiLoginID=this.loginId;var d={};d.cardNumber=t.CardJs("cardNumber").replace(/[^\d]/g,""),d.month=t.CardJs("expiryMonth").replace(/[^\d]/g,""),d.year=t.CardJs("expiryYear").replace(/[^\d]/g,""),d.cardCode=document.getElementById("cvv").value.replace(/[^\d]/g,"");var r={};return r.authData=a,r.cardData=d,document.getElementById("card_button").disabled=!0,document.querySelector("#card_button > svg").classList.remove("hidden"),document.querySelector("#card_button > span").classList.add("hidden"),Accept.dispatchData(r,this.responseHandler),!1}responseHandler(e){if(e.messages.resultCode==="Error"){var t=0,a=$("#errors");a.show().html("

"+e.messages.message[t].code+": "+e.messages.message[t].text+"

"),document.getElementById("card_button").disabled=!1,document.querySelector("#card_button > svg").classList.add("hidden"),document.querySelector("#card_button > span").classList.remove("hidden")}else e.messages.resultCode==="Ok"&&(document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue,document.getElementById("server_response").submit());return!1}handle(){return this.cardButton.addEventListener("click",()=>{this.cardButton.disabled=!this.cardButton.disabled,this.handleAuthorization()}),this}}const c=document.querySelector('meta[name="authorize-public-key"]').content,o=document.querySelector('meta[name="authorize-login-id"]').content,s=document.querySelector('meta[name="authnet-require-cvv"]').content;new n(c,o).handle(); diff --git a/public/build/assets/authorize-credit-card-payment-222655bd.js b/public/build/assets/authorize-credit-card-payment-222655bd.js new file mode 100644 index 000000000000..77e6ff82fcac --- /dev/null +++ b/public/build/assets/authorize-credit-card-payment-222655bd.js @@ -0,0 +1,9 @@ +var m=Object.defineProperty;var y=(n,e,t)=>e in n?m(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var o=(n,e,t)=>(y(n,typeof e!="symbol"?e+"":e,t),t);import{i as h,w as g}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class p{constructor(e,t){o(this,"handleAuthorization",()=>{var c,l,s,i;if(this.cvvRequired=="1"&&document.getElementById("cvv").value.length<3){const r=document.getElementById("errors");r&&(r.innerText="CVV is required",r.style.display="block"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");return}var e={};e.clientKey=this.publicKey,e.apiLoginID=this.loginId;var t={};t.cardNumber=(c=this.sc.value("number"))==null?void 0:c.replace(/[^\d]/g,""),t.month=(l=this.sc.value("month"))==null?void 0:l.replace(/[^\d]/g,""),t.year=`20${(s=this.sc.value("year"))==null?void 0:s.replace(/[^\d]/g,"")}`,t.cardCode=(i=this.sc.value("cvv"))==null?void 0:i.replace(/[^\d]/g,"");var a={};return a.authData=e,a.cardData=t,document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),Accept.dispatchData(a,this.responseHandler),!1});o(this,"responseHandler",e=>{if(e.messages.resultCode==="Error"){var t=0;const a=document.getElementById("errors");a&&(a.innerText=`${e.messages.message[t].code}: ${e.messages.message[t].text}`,a.style.display="block"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}else if(e.messages.resultCode==="Ok"){document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue;let a=document.querySelector("input[name=token-billing-checkbox]:checked");a&&(document.getElementById("store_card").value=a.value),document.getElementById("server_response").submit()}return!1});o(this,"handle",()=>{Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(a=>a.addEventListener("click",d=>{document.getElementById("save-card--container").style.display="none",document.getElementById("authorize--credit-card-container").style.display="none",document.getElementById("token").value=d.target.dataset.token}));let e=document.getElementById("toggle-payment-with-credit-card");e&&e.addEventListener("click",()=>{document.getElementById("save-card--container").style.display="grid",document.getElementById("authorize--credit-card-container").style.display="flex",document.getElementById("token").value=null});let t=document.getElementById("pay-now");return t&&t.addEventListener("click",a=>{let d=document.getElementById("token");d.value?this.handlePayNowAction(d.value):this.handleAuthorization()}),this});this.publicKey=e,this.loginId=t,this.cardHolderName=document.getElementById("cardholder_name"),this.sc=createSimpleCard({fields:{card:{number:"#number",date:"#date",cvv:"#cvv"}}}),this.sc.mount(),this.cvvRequired=document.querySelector('meta[name="authnet-require-cvv"]').content}handlePayNowAction(e){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),document.getElementById("token").value=e,document.getElementById("server_response").submit()}}function u(){const n=document.querySelector('meta[name="authorize-public-key"]').content,e=document.querySelector('meta[name="authorize-login-id"]').content;new p(n,e).handle()}h()?u():g("#authorize-net-credit-card-payment").then(()=>u()); diff --git a/public/build/assets/authorize-credit-card-payment-a217579b.js b/public/build/assets/authorize-credit-card-payment-a217579b.js deleted file mode 100644 index 1f462a77a11e..000000000000 --- a/public/build/assets/authorize-credit-card-payment-a217579b.js +++ /dev/null @@ -1,9 +0,0 @@ -var l=Object.defineProperty;var c=(d,e,t)=>e in d?l(d,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):d[e]=t;var o=(d,e,t)=>(c(d,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class s{constructor(e,t){o(this,"handleAuthorization",()=>{if(m=="1"&&document.getElementById("cvv").value.length<3){var e=$("#errors");e.show().html("

CVV is required

"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");return}var t=$("#my-card"),n={};n.clientKey=this.publicKey,n.apiLoginID=this.loginId;var a={};a.cardNumber=t.CardJs("cardNumber").replace(/[^\d]/g,""),a.month=t.CardJs("expiryMonth").replace(/[^\d]/g,""),a.year=t.CardJs("expiryYear").replace(/[^\d]/g,""),a.cardCode=document.getElementById("cvv").value.replace(/[^\d]/g,"");var r={};return r.authData=n,r.cardData=a,document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),Accept.dispatchData(r,this.responseHandler),!1});o(this,"responseHandler",e=>{if(e.messages.resultCode==="Error"){var t=0,n=$("#errors");n.show().html("

"+e.messages.message[t].code+": "+e.messages.message[t].text+"

"),document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}else if(e.messages.resultCode==="Ok"){document.getElementById("dataDescriptor").value=e.opaqueData.dataDescriptor,document.getElementById("dataValue").value=e.opaqueData.dataValue;let a=document.querySelector("input[name=token-billing-checkbox]:checked");a&&(document.getElementById("store_card").value=a.value),document.getElementById("server_response").submit()}return!1});o(this,"handle",()=>{Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(n=>n.addEventListener("click",a=>{document.getElementById("save-card--container").style.display="none",document.getElementById("authorize--credit-card-container").style.display="none",document.getElementById("token").value=a.target.dataset.token}));let e=document.getElementById("toggle-payment-with-credit-card");e&&e.addEventListener("click",()=>{document.getElementById("save-card--container").style.display="grid",document.getElementById("authorize--credit-card-container").style.display="flex",document.getElementById("token").value=null});let t=document.getElementById("pay-now");return t&&t.addEventListener("click",n=>{let a=document.getElementById("token");a.value?this.handlePayNowAction(a.value):this.handleAuthorization()}),this});this.publicKey=e,this.loginId=t,this.cardHolderName=document.getElementById("cardholder_name")}handlePayNowAction(e){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),document.getElementById("token").value=e,document.getElementById("server_response").submit()}}const u=document.querySelector('meta[name="authorize-public-key"]').content,i=document.querySelector('meta[name="authorize-login-id"]').content,m=document.querySelector('meta[name="authnet-require-cvv"]').content;new s(u,i).handle(); diff --git a/public/build/assets/authorize-stripe-acss-f6bd46c1.js b/public/build/assets/authorize-stripe-acss-f6bd46c1.js new file mode 100644 index 000000000000..34c2126d897c --- /dev/null +++ b/public/build/assets/authorize-stripe-acss-f6bd46c1.js @@ -0,0 +1,9 @@ +import{w as y}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */y("#stripe-acss-authorize").then(()=>f());function f(){var i,l,o;let n;const a=(i=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:i.content,r=(l=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:l.content;a&&a.length>0?n=Stripe(r,{stripeAccount:a}):n=Stripe(r);const c=document.getElementById("acss-name"),s=document.getElementById("acss-email-address"),t=document.getElementById("authorize-acss"),d=(o=document.querySelector('meta[name="stripe-pi-client-secret"]'))==null?void 0:o.content,e=document.getElementById("errors");t.addEventListener("click",async u=>{u.preventDefault(),e.hidden=!0,t.disabled=!0;const m=/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;if(s.value.length<3||!s.value.match(m)){e.textContent="Please enter a valid email address.",e.hidden=!1,t.disabled=!1;return}if(c.value.length<3){e.textContent="Please enter a name for the account holder.",e.hidden=!1,t.disabled=!1;return}const{setupIntent:p,error:h}=await n.confirmAcssDebitSetup(d,{payment_method:{billing_details:{name:c.value,email:s.value}}});document.getElementById("gateway_response").value=JSON.stringify(p??h),document.getElementById("server_response").submit()})} diff --git a/public/build/assets/braintree-ach-3d6b328f.js b/public/build/assets/braintree-ach-3d6b328f.js new file mode 100644 index 000000000000..e6fab3d9fe0b --- /dev/null +++ b/public/build/assets/braintree-ach-3d6b328f.js @@ -0,0 +1,9 @@ +import{i as l,w as c}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */function i(){var o;window.braintree.client.create({authorization:(o=document.querySelector('meta[name="client-token"]'))==null?void 0:o.content}).then(function(t){return braintree.usBankAccount.create({client:t})}).then(function(t){var a;(a=document.getElementById("authorize-bank-account"))==null||a.addEventListener("click",r=>{r.target.parentElement.disabled=!0,document.getElementById("errors").hidden=!0,document.getElementById("errors").textContent="";let n={accountNumber:document.getElementById("account-number").value,routingNumber:document.getElementById("routing-number").value,accountType:document.querySelector('input[name="account-type"]:checked').value,ownershipType:document.querySelector('input[name="ownership-type"]:checked').value,billingAddress:{streetAddress:document.getElementById("billing-street-address").value,extendedAddress:document.getElementById("billing-extended-address").value,locality:document.getElementById("billing-locality").value,region:document.getElementById("billing-region").value,postalCode:document.getElementById("billing-postal-code").value}};if(n.ownershipType==="personal"){let e=document.getElementById("account-holder-name").value.split(" ",2);n.firstName=e[0],n.lastName=e[1]}else n.businessName=document.getElementById("account-holder-name").value;t.tokenize({bankDetails:n,mandateText:'By clicking ["Checkout"], I authorize Braintree, a service of PayPal, on behalf of [your business name here] (i) to verify my bank account information using bank information and consumer reports and (ii) to debit my bank account.'}).then(function(e){document.querySelector("input[name=nonce]").value=e.nonce,document.getElementById("server_response").submit()}).catch(function(e){r.target.parentElement.disabled=!1,document.getElementById("errors").textContent=`${e.details.originalError.message} ${e.details.originalError.details.originalError[0].message}`,document.getElementById("errors").hidden=!1})})}).catch(function(t){document.getElementById("errors").textContent=t.message,document.getElementById("errors").hidden=!1})}l()?i():c("#braintree-ach-authorize").then(()=>i()); diff --git a/public/build/assets/braintree-ach-b29d040e.js b/public/build/assets/braintree-ach-b29d040e.js deleted file mode 100644 index 326e0c7aa173..000000000000 --- a/public/build/assets/braintree-ach-b29d040e.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com). - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */var a;window.braintree.client.create({authorization:(a=document.querySelector('meta[name="client-token"]'))==null?void 0:a.content}).then(function(t){return braintree.usBankAccount.create({client:t})}).then(function(t){var o;(o=document.getElementById("authorize-bank-account"))==null||o.addEventListener("click",r=>{r.target.parentElement.disabled=!0,document.getElementById("errors").hidden=!0,document.getElementById("errors").textContent="";let n={accountNumber:document.getElementById("account-number").value,routingNumber:document.getElementById("routing-number").value,accountType:document.querySelector('input[name="account-type"]:checked').value,ownershipType:document.querySelector('input[name="ownership-type"]:checked').value,billingAddress:{streetAddress:document.getElementById("billing-street-address").value,extendedAddress:document.getElementById("billing-extended-address").value,locality:document.getElementById("billing-locality").value,region:document.getElementById("billing-region").value,postalCode:document.getElementById("billing-postal-code").value}};if(n.ownershipType==="personal"){let e=document.getElementById("account-holder-name").value.split(" ",2);n.firstName=e[0],n.lastName=e[1]}else n.businessName=document.getElementById("account-holder-name").value;t.tokenize({bankDetails:n,mandateText:'By clicking ["Checkout"], I authorize Braintree, a service of PayPal, on behalf of [your business name here] (i) to verify my bank account information using bank information and consumer reports and (ii) to debit my bank account.'}).then(function(e){document.querySelector("input[name=nonce]").value=e.nonce,document.getElementById("server_response").submit()}).catch(function(e){r.target.parentElement.disabled=!1,document.getElementById("errors").textContent=`${e.details.originalError.message} ${e.details.originalError.details.originalError[0].message}`,document.getElementById("errors").hidden=!1})})}).catch(function(t){document.getElementById("errors").textContent=t.message,document.getElementById("errors").hidden=!1}); diff --git a/public/build/assets/braintree-credit-card-1c3f3108.js b/public/build/assets/braintree-credit-card-1c3f3108.js deleted file mode 100644 index f9a4e882261c..000000000000 --- a/public/build/assets/braintree-credit-card-1c3f3108.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com). - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class i{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(e,n){window.braintree.dataCollector.create({client:n,paypal:!0},function(t,a){t||(document.querySelector("input[name=client-data]").value=a.deviceData)})})}mountBraintreePaymentWidget(){window.braintree.dropin.create({authorization:document.querySelector("meta[name=client-token]").content,container:"#dropin-container",threeDSecure:document.querySelector("input[name=threeds_enable]").value.toLowerCase()==="true"},this.handleCallback)}handleCallback(e,n){if(e){console.error(e);return}let t=document.getElementById("pay-now");params=JSON.parse(document.querySelector("input[name=threeds]").value),t.addEventListener("click",()=>{n.requestPaymentMethod({threeDSecure:{challengeRequested:!0,amount:params.amount,email:params.email,billingAddress:{givenName:params.billingAddress.givenName,surname:params.billingAddress.surname,phoneNumber:params.billingAddress.phoneNumber,streetAddress:params.billingAddress.streetAddress,extendedAddress:params.billingAddress.extendedAddress,locality:params.billingAddress.locality,region:params.billingAddress.region,postalCode:params.billingAddress.postalCode,countryCodeAlpha2:params.billingAddress.countryCodeAlpha2}}},function(a,r){if(a){console.log(a),dropin.clearSelectedPaymentMethod(),alert("There was a problem verifying this card, please contact your merchant");return}if(document.querySelector("input[name=threeds_enable]").value==="true"&&!r.liabilityShifted){console.log("Liability did not shift",r),alert("There was a problem verifying this card, please contact your merchant");return}t.disabled=!0,t.querySelector("svg").classList.remove("hidden"),t.querySelector("span").classList.add("hidden"),document.querySelector("input[name=gateway_response]").value=JSON.stringify(r);let d=document.querySelector('input[name="token-billing-checkbox"]:checked');d&&(document.querySelector('input[name="store_card"]').value=d.value),document.getElementById("server-response").submit()})})}handle(){this.initBraintreeDataCollector(),this.mountBraintreePaymentWidget(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(n=>n.addEventListener("click",t=>{document.getElementById("dropin-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",n=>{document.getElementById("dropin-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",document.getElementById("pay-now-with-token").classList.add("hidden"),document.getElementById("pay-now").classList.remove("hidden")});let e=document.getElementById("pay-now-with-token");e.addEventListener("click",n=>{e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}}new i().handle(); diff --git a/public/build/assets/braintree-credit-card-60bd8878.js b/public/build/assets/braintree-credit-card-60bd8878.js new file mode 100644 index 000000000000..d2c7d7f9820e --- /dev/null +++ b/public/build/assets/braintree-credit-card-60bd8878.js @@ -0,0 +1,9 @@ +import{i as l,w as s}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class c{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(t,r){window.braintree.dataCollector.create({client:r,paypal:!0},function(n,e){n||(document.querySelector("input[name=client-data]").value=e.deviceData)})})}mountBraintreePaymentWidget(){window.braintree.dropin.create({authorization:document.querySelector("meta[name=client-token]").content,container:"#dropin-container",threeDSecure:document.querySelector("input[name=threeds_enable]").value.toLowerCase()==="true"},this.handleCallback)}handleCallback(t,r){if(t){console.error(t);return}let n=document.getElementById("pay-now"),e=JSON.parse(document.querySelector("input[name=threeds]").value);n.addEventListener("click",()=>{r.requestPaymentMethod({threeDSecure:{challengeRequested:!0,amount:e.amount,email:e.email,billingAddress:{givenName:e.billingAddress.givenName,surname:e.billingAddress.surname,phoneNumber:e.billingAddress.phoneNumber,streetAddress:e.billingAddress.streetAddress,extendedAddress:e.billingAddress.extendedAddress,locality:e.billingAddress.locality,region:e.billingAddress.region,postalCode:e.billingAddress.postalCode,countryCodeAlpha2:e.billingAddress.countryCodeAlpha2}}},function(i,a){if(i){console.log(i),dropin.clearSelectedPaymentMethod(),alert("There was a problem verifying this card, please contact your merchant");return}if(document.querySelector("input[name=threeds_enable]").value==="true"&&!a.liabilityShifted){console.log("Liability did not shift",a),alert("There was a problem verifying this card, please contact your merchant");return}n.disabled=!0,n.querySelector("svg").classList.remove("hidden"),n.querySelector("span").classList.add("hidden"),document.querySelector("input[name=gateway_response]").value=JSON.stringify(a);let d=document.querySelector('input[name="token-billing-checkbox"]:checked');d&&(document.querySelector('input[name="store_card"]').value=d.value),document.getElementById("server-response").submit()})})}handle(){this.initBraintreeDataCollector(),this.mountBraintreePaymentWidget(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(r=>r.addEventListener("click",n=>{document.getElementById("dropin-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",r=>{document.getElementById("dropin-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",document.getElementById("pay-now-with-token").classList.add("hidden"),document.getElementById("pay-now").classList.remove("hidden")});let t=document.getElementById("pay-now-with-token");t.addEventListener("click",r=>{t.disabled=!0,t.querySelector("svg").classList.remove("hidden"),t.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}}function o(){new c().handle()}l()?o():s("#braintree-credit-card-payment","meta[name=client-token]").then(()=>o()); diff --git a/public/build/assets/braintree-paypal-45391805.js b/public/build/assets/braintree-paypal-f78ad64b.js similarity index 69% rename from public/build/assets/braintree-paypal-45391805.js rename to public/build/assets/braintree-paypal-f78ad64b.js index 662aab2a1bc2..98e40ff55193 100644 --- a/public/build/assets/braintree-paypal-45391805.js +++ b/public/build/assets/braintree-paypal-f78ad64b.js @@ -1,4 +1,4 @@ -/** +import{i as s,w as u}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com). * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class a{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(e,t){window.braintree.dataCollector.create({client:t,paypal:!0},function(n,o){n||(document.querySelector("input[name=client-data]").value=o.deviceData)})})}static getPaymentDetails(){return{flow:"vault"}}static handleErrorMessage(e){let t=document.getElementById("errors");t.innerText=e,t.hidden=!1}handlePaymentWithToken(){Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",n=>{document.getElementById("paypal-button").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")}));let e=document.getElementById("pay-now-with-token");e.addEventListener("click",t=>{e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}handle(){this.initBraintreeDataCollector(),this.handlePaymentWithToken(),braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content}).then(function(e){return braintree.paypalCheckout.create({client:e})}).then(function(e){return e.loadPayPalSDK({vault:!0}).then(function(t){return paypal.Buttons({fundingSource:paypal.FUNDING.PAYPAL,createBillingAgreement:function(){return t.createPayment(a.getPaymentDetails())},onApprove:function(n,o){return t.tokenizePayment(n).then(function(i){let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.querySelector("input[name=gateway_response]").value=JSON.stringify(i),document.getElementById("server-response").submit()})},onCancel:function(n){},onError:function(n){console.log(n.message),a.handleErrorMessage(n.message)}}).render("#paypal-button")})}).catch(function(e){console.log(e.message),a.handleErrorMessage(e.message)})}}new a().handle(); + */class a{initBraintreeDataCollector(){window.braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content},function(e,t){window.braintree.dataCollector.create({client:t,paypal:!0},function(n,o){n||(document.querySelector("input[name=client-data]").value=o.deviceData)})})}static getPaymentDetails(){return{flow:"vault"}}static handleErrorMessage(e){let t=document.getElementById("errors");t.innerText=e,t.hidden=!1}handlePaymentWithToken(){Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",n=>{document.getElementById("paypal-button").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token,document.getElementById("pay-now-with-token").classList.remove("hidden"),document.getElementById("pay-now").classList.add("hidden")}));let e=document.getElementById("pay-now-with-token");e.addEventListener("click",t=>{e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})}handle(){this.initBraintreeDataCollector(),this.handlePaymentWithToken(),braintree.client.create({authorization:document.querySelector("meta[name=client-token]").content}).then(function(e){return braintree.paypalCheckout.create({client:e})}).then(function(e){return e.loadPayPalSDK({vault:!0}).then(function(t){return paypal.Buttons({fundingSource:paypal.FUNDING.PAYPAL,createBillingAgreement:function(){return t.createPayment(a.getPaymentDetails())},onApprove:function(n,o){return t.tokenizePayment(n).then(function(d){var i,c;(i=document.querySelector("#paypal-button"))==null||i.classList.add("hidden"),(c=document.querySelector("#paypal-spinner"))==null||c.classList.remove("hidden");let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.querySelector("input[name=gateway_response]").value=JSON.stringify(d),document.getElementById("server-response").submit()})},onCancel:function(n){},onError:function(n){console.log(n.message),a.handleErrorMessage(n.message)}}).render("#paypal-button")})}).catch(function(e){console.log(e.message),a.handleErrorMessage(e.message)})}}function l(){new a().handle()}s()?l():u("#braintree-paypal-payment").then(()=>l()); diff --git a/public/build/assets/checkout-credit-card-2cca8b36.js b/public/build/assets/checkout-credit-card-2cca8b36.js new file mode 100644 index 000000000000..cc32bdcba638 --- /dev/null +++ b/public/build/assets/checkout-credit-card-2cca8b36.js @@ -0,0 +1,9 @@ +import{i as s,w as i}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class a{constructor(){this.tokens=[]}handlePaymentUsingToken(t){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token}handlePaymentUsingCreditCard(t){document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="";const e=document.getElementById("pay-button"),d=document.querySelector('meta[name="public-key"]').content??"",o=document.getElementById("payment-form");Frames.init(d),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,function(n){e.disabled=!Frames.isCardValid()}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED,function(n){e.disabled=!1}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,function(n){e.disabled=!0,document.querySelector('input[name="gateway_response"]').value=JSON.stringify(n),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}),o.addEventListener("submit",function(n){n.preventDefault(),e.disabled=!0,Frames.submitCard()})}completePaymentUsingToken(t){let e=document.getElementById("pay-now-with-token");e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}handle(){this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",this.handlePaymentUsingToken)),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}function r(){new a().handle()}s()?r():i("#checkout-credit-card-payment").then(()=>new a().handle()); diff --git a/public/build/assets/checkout-credit-card-8a04938c.js b/public/build/assets/checkout-credit-card-8a04938c.js deleted file mode 100644 index 0125ca29bec6..000000000000 --- a/public/build/assets/checkout-credit-card-8a04938c.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class o{constructor(){this.tokens=[]}mountFrames(){console.log("Mount checkout frames..")}handlePaymentUsingToken(t){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token}handlePaymentUsingCreditCard(t){document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="";const e=document.getElementById("pay-button"),d=document.querySelector('meta[name="public-key"]').content??"",a=document.getElementById("payment-form");Frames.init(d),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,function(n){e.disabled=!Frames.isCardValid()}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED,function(n){e.disabled=!1}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,function(n){e.disabled=!0,document.querySelector('input[name="gateway_response"]').value=JSON.stringify(n),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}),a.addEventListener("submit",function(n){n.preventDefault(),e.disabled=!0,Frames.submitCard()})}completePaymentUsingToken(t){let e=document.getElementById("pay-now-with-token");e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}handle(){this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",this.handlePaymentUsingToken)),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}new o().handle(); diff --git a/public/build/assets/eway-credit-card-62ce5f3b.js b/public/build/assets/eway-credit-card-150298fa.js similarity index 81% rename from public/build/assets/eway-credit-card-62ce5f3b.js rename to public/build/assets/eway-credit-card-150298fa.js index 3c3ceed101ae..c666a834e504 100644 --- a/public/build/assets/eway-credit-card-62ce5f3b.js +++ b/public/build/assets/eway-credit-card-150298fa.js @@ -1,4 +1,4 @@ -/** +import{i as d,w as n}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com). * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class i{constructor(){this.cardStyles="padding: 2px; border: 1px solid #AAA; border-radius: 3px; height: 34px; width: 100%;",this.errorCodes=new Map,this.errorCodes.set("V6000","Validation error"),this.errorCodes.set("V6001","Invalid CustomerIP"),this.errorCodes.set("V6002","Invalid DeviceID"),this.errorCodes.set("V6003","Invalid Request PartnerID"),this.errorCodes.set("V6004","Invalid Request Method"),this.errorCodes.set("V6010","Invalid TransactionType, account not certified for eCome only MOTO or Recurring available"),this.errorCodes.set("V6011","Invalid Payment TotalAmount"),this.errorCodes.set("V6012","Invalid Payment InvoiceDescription"),this.errorCodes.set("V6013","Invalid Payment InvoiceNumber"),this.errorCodes.set("V6014","Invalid Payment InvoiceReference"),this.errorCodes.set("V6015","Invalid Payment CurrencyCode"),this.errorCodes.set("V6016","Payment Required"),this.errorCodes.set("V6017","Payment CurrencyCode Required"),this.errorCodes.set("V6018","Unknown Payment CurrencyCode"),this.errorCodes.set("V6019","Cardholder identity authentication required"),this.errorCodes.set("V6020","Cardholder Input Required"),this.errorCodes.set("V6021","EWAY_CARDHOLDERNAME Required"),this.errorCodes.set("V6022","EWAY_CARDNUMBER Required"),this.errorCodes.set("V6023","EWAY_CARDCVN Required"),this.errorCodes.set("V6024","Cardholder Identity Authentication One Time Password Not Active Yet"),this.errorCodes.set("V6025","PIN Required"),this.errorCodes.set("V6033","Invalid Expiry Date"),this.errorCodes.set("V6034","Invalid Issue Number"),this.errorCodes.set("V6035","Invalid Valid From Date"),this.errorCodes.set("V6039","Invalid Network Token Status"),this.errorCodes.set("V6040","Invalid TokenCustomerID"),this.errorCodes.set("V6041","Customer Required"),this.errorCodes.set("V6042","Customer FirstName Required"),this.errorCodes.set("V6043","Customer LastName Required"),this.errorCodes.set("V6044","Customer CountryCode Required"),this.errorCodes.set("V6045","Customer Title Required"),this.errorCodes.set("V6046","TokenCustomerID Required"),this.errorCodes.set("V6047","RedirectURL Required"),this.errorCodes.set("V6048","CheckoutURL Required when CheckoutPayment specified"),this.errorCodes.set("V6049","nvalid Checkout URL"),this.errorCodes.set("V6051","Invalid Customer FirstName"),this.errorCodes.set("V6052","Invalid Customer LastName"),this.errorCodes.set("V6053","Invalid Customer CountryCode"),this.errorCodes.set("V6058","Invalid Customer Title"),this.errorCodes.set("V6059","Invalid RedirectURL"),this.errorCodes.set("V6060","Invalid TokenCustomerID"),this.errorCodes.set("V6061","Invalid Customer Reference"),this.errorCodes.set("V6062","Invalid Customer CompanyName"),this.errorCodes.set("V6063","Invalid Customer JobDescription"),this.errorCodes.set("V6064","Invalid Customer Street1"),this.errorCodes.set("V6065","Invalid Customer Street2"),this.errorCodes.set("V6066","Invalid Customer City"),this.errorCodes.set("V6067","Invalid Customer State"),this.errorCodes.set("V6068","Invalid Customer PostalCode"),this.errorCodes.set("V6069","Invalid Customer Email"),this.errorCodes.set("V6070","Invalid Customer Phone"),this.errorCodes.set("V6071","Invalid Customer Mobile"),this.errorCodes.set("V6072","Invalid Customer Comments"),this.errorCodes.set("V6073","Invalid Customer Fax"),this.errorCodes.set("V6074","Invalid Customer URL"),this.errorCodes.set("V6075","Invalid ShippingAddress FirstName"),this.errorCodes.set("V6076","Invalid ShippingAddress LastName"),this.errorCodes.set("V6077","Invalid ShippingAddress Street1"),this.errorCodes.set("V6078","Invalid ShippingAddress Street2"),this.errorCodes.set("V6079","Invalid ShippingAddress City"),this.errorCodes.set("V6080","Invalid ShippingAddress State"),this.errorCodes.set("V6081","Invalid ShippingAddress PostalCode"),this.errorCodes.set("V6082","Invalid ShippingAddress Email"),this.errorCodes.set("V6083","Invalid ShippingAddress Phone"),this.errorCodes.set("V6084","Invalid ShippingAddress Country"),this.errorCodes.set("V6085","Invalid ShippingAddress ShippingMethod"),this.errorCodes.set("V6086","Invalid ShippingAddress Fax"),this.errorCodes.set("V6091","Unknown Customer CountryCode"),this.errorCodes.set("V6092","Unknown ShippingAddress CountryCode"),this.errorCodes.set("V6093","Insufficient Address Information"),this.errorCodes.set("V6100","Invalid EWAY_CARDNAME"),this.errorCodes.set("V6101","Invalid EWAY_CARDEXPIRYMONTH"),this.errorCodes.set("V6102","Invalid EWAY_CARDEXPIRYYEAR"),this.errorCodes.set("V6103","Invalid EWAY_CARDSTARTMONTH"),this.errorCodes.set("V6104","Invalid EWAY_CARDSTARTYEAR"),this.errorCodes.set("V6105","Invalid EWAY_CARDISSUENUMBER"),this.errorCodes.set("V6106","Invalid EWAY_CARDCVN"),this.errorCodes.set("V6107","Invalid EWAY_ACCESSCODE"),this.errorCodes.set("V6108","Invalid CustomerHostAddress"),this.errorCodes.set("V6109","Invalid UserAgent"),this.errorCodes.set("V6110","Invalid EWAY_CARDNUMBER"),this.errorCodes.set("V6111","Unauthorised API Access, Account Not PCI Certified"),this.errorCodes.set("V6112","Redundant card details other than expiry year and month"),this.errorCodes.set("V6113","Invalid transaction for refund"),this.errorCodes.set("V6114","Gateway validation error"),this.errorCodes.set("V6115","Invalid DirectRefundRequest, Transaction ID"),this.errorCodes.set("V6116","Invalid card data on original TransactionID"),this.errorCodes.set("V6117","Invalid CreateAccessCodeSharedRequest, FooterText"),this.errorCodes.set("V6118","Invalid CreateAccessCodeSharedRequest, HeaderText"),this.errorCodes.set("V6119","Invalid CreateAccessCodeSharedRequest, Language"),this.errorCodes.set("V6120","Invalid CreateAccessCodeSharedRequest, LogoUrl"),this.errorCodes.set("V6121","Invalid TransactionSearch, Filter Match Type"),this.errorCodes.set("V6122","Invalid TransactionSearch, Non numeric Transaction ID"),this.errorCodes.set("V6123","Invalid TransactionSearch,no TransactionID or AccessCode specified"),this.errorCodes.set("V6124","Invalid Line Items. The line items have been provided however the totals do not match the TotalAmount field"),this.errorCodes.set("V6125","Selected Payment Type not enabled"),this.errorCodes.set("V6126","Invalid encrypted card number, decryption failed"),this.errorCodes.set("V6127","Invalid encrypted cvn, decryption failed"),this.errorCodes.set("V6128","Invalid Method for Payment Type"),this.errorCodes.set("V6129","Transaction has not been authorised for Capture/Cancellation"),this.errorCodes.set("V6130","Generic customer information error"),this.errorCodes.set("V6131","Generic shipping information error"),this.errorCodes.set("V6132","Transaction has already been completed or voided, operation not permitted"),this.errorCodes.set("V6133","Checkout not available for Payment Type"),this.errorCodes.set("V6134","Invalid Auth Transaction ID for Capture/Void"),this.errorCodes.set("V6135","PayPal Error Processing Refund"),this.errorCodes.set("V6136","Original transaction does not exist or state is incorrect"),this.errorCodes.set("V6140","Merchant account is suspended"),this.errorCodes.set("V6141","Invalid PayPal account details or API signature"),this.errorCodes.set("V6142","Authorise not available for Bank/Branch"),this.errorCodes.set("V6143","Invalid Public Key"),this.errorCodes.set("V6144","Method not available with Public API Key Authentication"),this.errorCodes.set("V6145","Credit Card not allow if Token Customer ID is provided with Public API Key Authentication"),this.errorCodes.set("V6146","Client Side Encryption Key Missing or Invalid"),this.errorCodes.set("V6147","Unable to Create One Time Code for Secure Field"),this.errorCodes.set("V6148","Secure Field has Expired"),this.errorCodes.set("V6149","Invalid Secure Field One Time Code"),this.errorCodes.set("V6150","Invalid Refund Amount"),this.errorCodes.set("V6151","Refund amount greater than original transaction"),this.errorCodes.set("V6152","Original transaction already refunded for total amount"),this.errorCodes.set("V6153","Card type not support by merchant"),this.errorCodes.set("V6154","Insufficent Funds Available For Refund"),this.errorCodes.set("V6155","Missing one or more fields in request"),this.errorCodes.set("V6160","Encryption Method Not Supported"),this.errorCodes.set("V6161","Encryption failed, missing or invalid key"),this.errorCodes.set("V6165","Invalid Click-to-Pay (Visa Checkout) data or decryption failed"),this.errorCodes.set("V6170","Invalid TransactionSearch, Invoice Number is not unique"),this.errorCodes.set("V6171","Invalid TransactionSearch, Invoice Number not found"),this.errorCodes.set("V6220","Three domain secure XID invalid"),this.errorCodes.set("V6221","Three domain secure ECI invalid"),this.errorCodes.set("V6222","Three domain secure AVV invalid"),this.errorCodes.set("V6223","Three domain secure XID is required"),this.errorCodes.set("V6224","Three Domain Secure ECI is required"),this.errorCodes.set("V6225","Three Domain Secure AVV is required"),this.errorCodes.set("V6226","Three Domain Secure AuthStatus is required"),this.errorCodes.set("V6227","Three Domain Secure AuthStatus invalid"),this.errorCodes.set("V6228","Three domain secure Version is required"),this.errorCodes.set("V6230","Three domain secure Directory Server Txn ID invalid"),this.errorCodes.set("V6231","Three domain secure Directory Server Txn ID is required"),this.errorCodes.set("V6232","Three domain secure Version is invalid"),this.errorCodes.set("V6501","Invalid Amex InstallementPlan"),this.errorCodes.set("V6502","Invalid Number Of Installements for Amex. Valid values are from 0 to 99 inclusive"),this.errorCodes.set("V6503","Merchant Amex ID required"),this.errorCodes.set("V6504","Invalid Merchant Amex ID"),this.errorCodes.set("V6505","Merchant Terminal ID required"),this.errorCodes.set("V6506","Merchant category code required"),this.errorCodes.set("V6507","Invalid merchant category code"),this.errorCodes.set("V6508","Amex 3D ECI required"),this.errorCodes.set("V6509","Invalid Amex 3D ECI"),this.errorCodes.set("V6510","Invalid Amex 3D verification value"),this.errorCodes.set("V6511","Invalid merchant location data"),this.errorCodes.set("V6512","Invalid merchant street address"),this.errorCodes.set("V6513","Invalid merchant city"),this.errorCodes.set("V6514","Invalid merchant country"),this.errorCodes.set("V6515","Invalid merchant phone"),this.errorCodes.set("V6516","Invalid merchant postcode"),this.errorCodes.set("V6517","Amex connection error"),this.errorCodes.set("V6518","Amex EC Card Details API returned invalid data"),this.errorCodes.set("V6520","Invalid or missing Amex Point Of Sale Data"),this.errorCodes.set("V6521","Invalid or missing Amex transaction date time"),this.errorCodes.set("V6522","Invalid or missing Amex Original transaction date time"),this.errorCodes.set("V6530","Credit Card Number in non Credit Card Field")}get groupFieldConfig(){var e,t,r,s,o;return{publicApiKey:(e=document.querySelector("meta[name=public-api-key]"))==null?void 0:e.content,fieldDivId:"eway-secure-panel",fieldType:"group",styles:"",layout:{fonts:["Lobster"],rows:[{styles:"",cells:[{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(t=document.querySelector("meta[name=translation-card-name]"))==null?void 0:t.content,styles:""},field:{fieldColSpan:8,fieldType:"name",styles:this.cardStyles,divStyles:"padding-left: 10px;"}},{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(r=document.querySelector("meta[name=translation-expiry_date]"))==null?void 0:r.content,styles:""},field:{fieldColSpan:8,fieldType:"expirytext",styles:this.cardStyles,divStyles:"padding-left: 10px;"}}]},{styles:"",cells:[{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(s=document.querySelector("meta[name=translation-card_number]"))==null?void 0:s.content,styles:""},field:{fieldColSpan:8,fieldType:"card",styles:this.cardStyles}},{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(o=document.querySelector("meta[name=translation-cvv]"))==null?void 0:o.content,styles:""},field:{fieldColSpan:8,fieldType:"cvn",styles:this.cardStyles}}]}]}}}securePanelCallback(e){if(document.getElementById("errors").hidden=!0,e.errors)return this.handleErrors(e.errors);document.getElementById("authorize-card")&&(document.getElementById("authorize-card").disabled=!1),document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!1),document.querySelector("input[name=securefieldcode]").value=e.secureFieldCode}handleErrors(e){let t=e.split(" "),r="";t.forEach(s=>{r=r.concat(this.errorCodes.get(s)+"
")}),document.getElementById("errors").innerHTML=r,document.getElementById("errors").hidden=!1}completeAuthorization(e){e.target.parentElement.disabled=!0,document.getElementById("server-response").submit()}completePaymentUsingToken(e){e.target.parentElement.disabled=!0,document.getElementById("server-response").submit()}completePaymentWithoutToken(e){e.target.parentElement.disabled=!0;let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}initialize(){this.eWAY=eWAY.setupSecureField(this.groupFieldConfig,e=>this.securePanelCallback(e))}handle(){var e,t;this.initialize(),(e=document.getElementById("authorize-card"))==null||e.addEventListener("click",r=>this.completeAuthorization(r)),Array.from(document.getElementsByClassName("toggle-payment-with-token")??[]).forEach(r=>r.addEventListener("click",s=>{document.getElementById("eway-secure-panel").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=s.target.dataset.token,document.getElementById("pay-now").disabled=!1})),document.getElementById("toggle-payment-with-credit-card")&&document.getElementById("toggle-payment-with-credit-card").addEventListener("click",r=>{document.getElementById("eway-secure-panel").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",document.getElementById("pay-now").disabled=!0}),(t=document.getElementById("pay-now"))==null||t.addEventListener("click",r=>document.querySelector("input[name=token]").value?this.completePaymentUsingToken(r):this.completePaymentWithoutToken(r))}}new i().handle(); + */class a{constructor(){this.cardStyles="padding: 2px; border: 1px solid #AAA; border-radius: 3px; height: 34px; width: 100%;",this.errorCodes=new Map,this.errorCodes.set("V6000","Validation error"),this.errorCodes.set("V6001","Invalid CustomerIP"),this.errorCodes.set("V6002","Invalid DeviceID"),this.errorCodes.set("V6003","Invalid Request PartnerID"),this.errorCodes.set("V6004","Invalid Request Method"),this.errorCodes.set("V6010","Invalid TransactionType, account not certified for eCome only MOTO or Recurring available"),this.errorCodes.set("V6011","Invalid Payment TotalAmount"),this.errorCodes.set("V6012","Invalid Payment InvoiceDescription"),this.errorCodes.set("V6013","Invalid Payment InvoiceNumber"),this.errorCodes.set("V6014","Invalid Payment InvoiceReference"),this.errorCodes.set("V6015","Invalid Payment CurrencyCode"),this.errorCodes.set("V6016","Payment Required"),this.errorCodes.set("V6017","Payment CurrencyCode Required"),this.errorCodes.set("V6018","Unknown Payment CurrencyCode"),this.errorCodes.set("V6019","Cardholder identity authentication required"),this.errorCodes.set("V6020","Cardholder Input Required"),this.errorCodes.set("V6021","EWAY_CARDHOLDERNAME Required"),this.errorCodes.set("V6022","EWAY_CARDNUMBER Required"),this.errorCodes.set("V6023","EWAY_CARDCVN Required"),this.errorCodes.set("V6024","Cardholder Identity Authentication One Time Password Not Active Yet"),this.errorCodes.set("V6025","PIN Required"),this.errorCodes.set("V6033","Invalid Expiry Date"),this.errorCodes.set("V6034","Invalid Issue Number"),this.errorCodes.set("V6035","Invalid Valid From Date"),this.errorCodes.set("V6039","Invalid Network Token Status"),this.errorCodes.set("V6040","Invalid TokenCustomerID"),this.errorCodes.set("V6041","Customer Required"),this.errorCodes.set("V6042","Customer FirstName Required"),this.errorCodes.set("V6043","Customer LastName Required"),this.errorCodes.set("V6044","Customer CountryCode Required"),this.errorCodes.set("V6045","Customer Title Required"),this.errorCodes.set("V6046","TokenCustomerID Required"),this.errorCodes.set("V6047","RedirectURL Required"),this.errorCodes.set("V6048","CheckoutURL Required when CheckoutPayment specified"),this.errorCodes.set("V6049","nvalid Checkout URL"),this.errorCodes.set("V6051","Invalid Customer FirstName"),this.errorCodes.set("V6052","Invalid Customer LastName"),this.errorCodes.set("V6053","Invalid Customer CountryCode"),this.errorCodes.set("V6058","Invalid Customer Title"),this.errorCodes.set("V6059","Invalid RedirectURL"),this.errorCodes.set("V6060","Invalid TokenCustomerID"),this.errorCodes.set("V6061","Invalid Customer Reference"),this.errorCodes.set("V6062","Invalid Customer CompanyName"),this.errorCodes.set("V6063","Invalid Customer JobDescription"),this.errorCodes.set("V6064","Invalid Customer Street1"),this.errorCodes.set("V6065","Invalid Customer Street2"),this.errorCodes.set("V6066","Invalid Customer City"),this.errorCodes.set("V6067","Invalid Customer State"),this.errorCodes.set("V6068","Invalid Customer PostalCode"),this.errorCodes.set("V6069","Invalid Customer Email"),this.errorCodes.set("V6070","Invalid Customer Phone"),this.errorCodes.set("V6071","Invalid Customer Mobile"),this.errorCodes.set("V6072","Invalid Customer Comments"),this.errorCodes.set("V6073","Invalid Customer Fax"),this.errorCodes.set("V6074","Invalid Customer URL"),this.errorCodes.set("V6075","Invalid ShippingAddress FirstName"),this.errorCodes.set("V6076","Invalid ShippingAddress LastName"),this.errorCodes.set("V6077","Invalid ShippingAddress Street1"),this.errorCodes.set("V6078","Invalid ShippingAddress Street2"),this.errorCodes.set("V6079","Invalid ShippingAddress City"),this.errorCodes.set("V6080","Invalid ShippingAddress State"),this.errorCodes.set("V6081","Invalid ShippingAddress PostalCode"),this.errorCodes.set("V6082","Invalid ShippingAddress Email"),this.errorCodes.set("V6083","Invalid ShippingAddress Phone"),this.errorCodes.set("V6084","Invalid ShippingAddress Country"),this.errorCodes.set("V6085","Invalid ShippingAddress ShippingMethod"),this.errorCodes.set("V6086","Invalid ShippingAddress Fax"),this.errorCodes.set("V6091","Unknown Customer CountryCode"),this.errorCodes.set("V6092","Unknown ShippingAddress CountryCode"),this.errorCodes.set("V6093","Insufficient Address Information"),this.errorCodes.set("V6100","Invalid EWAY_CARDNAME"),this.errorCodes.set("V6101","Invalid EWAY_CARDEXPIRYMONTH"),this.errorCodes.set("V6102","Invalid EWAY_CARDEXPIRYYEAR"),this.errorCodes.set("V6103","Invalid EWAY_CARDSTARTMONTH"),this.errorCodes.set("V6104","Invalid EWAY_CARDSTARTYEAR"),this.errorCodes.set("V6105","Invalid EWAY_CARDISSUENUMBER"),this.errorCodes.set("V6106","Invalid EWAY_CARDCVN"),this.errorCodes.set("V6107","Invalid EWAY_ACCESSCODE"),this.errorCodes.set("V6108","Invalid CustomerHostAddress"),this.errorCodes.set("V6109","Invalid UserAgent"),this.errorCodes.set("V6110","Invalid EWAY_CARDNUMBER"),this.errorCodes.set("V6111","Unauthorised API Access, Account Not PCI Certified"),this.errorCodes.set("V6112","Redundant card details other than expiry year and month"),this.errorCodes.set("V6113","Invalid transaction for refund"),this.errorCodes.set("V6114","Gateway validation error"),this.errorCodes.set("V6115","Invalid DirectRefundRequest, Transaction ID"),this.errorCodes.set("V6116","Invalid card data on original TransactionID"),this.errorCodes.set("V6117","Invalid CreateAccessCodeSharedRequest, FooterText"),this.errorCodes.set("V6118","Invalid CreateAccessCodeSharedRequest, HeaderText"),this.errorCodes.set("V6119","Invalid CreateAccessCodeSharedRequest, Language"),this.errorCodes.set("V6120","Invalid CreateAccessCodeSharedRequest, LogoUrl"),this.errorCodes.set("V6121","Invalid TransactionSearch, Filter Match Type"),this.errorCodes.set("V6122","Invalid TransactionSearch, Non numeric Transaction ID"),this.errorCodes.set("V6123","Invalid TransactionSearch,no TransactionID or AccessCode specified"),this.errorCodes.set("V6124","Invalid Line Items. The line items have been provided however the totals do not match the TotalAmount field"),this.errorCodes.set("V6125","Selected Payment Type not enabled"),this.errorCodes.set("V6126","Invalid encrypted card number, decryption failed"),this.errorCodes.set("V6127","Invalid encrypted cvn, decryption failed"),this.errorCodes.set("V6128","Invalid Method for Payment Type"),this.errorCodes.set("V6129","Transaction has not been authorised for Capture/Cancellation"),this.errorCodes.set("V6130","Generic customer information error"),this.errorCodes.set("V6131","Generic shipping information error"),this.errorCodes.set("V6132","Transaction has already been completed or voided, operation not permitted"),this.errorCodes.set("V6133","Checkout not available for Payment Type"),this.errorCodes.set("V6134","Invalid Auth Transaction ID for Capture/Void"),this.errorCodes.set("V6135","PayPal Error Processing Refund"),this.errorCodes.set("V6136","Original transaction does not exist or state is incorrect"),this.errorCodes.set("V6140","Merchant account is suspended"),this.errorCodes.set("V6141","Invalid PayPal account details or API signature"),this.errorCodes.set("V6142","Authorise not available for Bank/Branch"),this.errorCodes.set("V6143","Invalid Public Key"),this.errorCodes.set("V6144","Method not available with Public API Key Authentication"),this.errorCodes.set("V6145","Credit Card not allow if Token Customer ID is provided with Public API Key Authentication"),this.errorCodes.set("V6146","Client Side Encryption Key Missing or Invalid"),this.errorCodes.set("V6147","Unable to Create One Time Code for Secure Field"),this.errorCodes.set("V6148","Secure Field has Expired"),this.errorCodes.set("V6149","Invalid Secure Field One Time Code"),this.errorCodes.set("V6150","Invalid Refund Amount"),this.errorCodes.set("V6151","Refund amount greater than original transaction"),this.errorCodes.set("V6152","Original transaction already refunded for total amount"),this.errorCodes.set("V6153","Card type not support by merchant"),this.errorCodes.set("V6154","Insufficent Funds Available For Refund"),this.errorCodes.set("V6155","Missing one or more fields in request"),this.errorCodes.set("V6160","Encryption Method Not Supported"),this.errorCodes.set("V6161","Encryption failed, missing or invalid key"),this.errorCodes.set("V6165","Invalid Click-to-Pay (Visa Checkout) data or decryption failed"),this.errorCodes.set("V6170","Invalid TransactionSearch, Invoice Number is not unique"),this.errorCodes.set("V6171","Invalid TransactionSearch, Invoice Number not found"),this.errorCodes.set("V6220","Three domain secure XID invalid"),this.errorCodes.set("V6221","Three domain secure ECI invalid"),this.errorCodes.set("V6222","Three domain secure AVV invalid"),this.errorCodes.set("V6223","Three domain secure XID is required"),this.errorCodes.set("V6224","Three Domain Secure ECI is required"),this.errorCodes.set("V6225","Three Domain Secure AVV is required"),this.errorCodes.set("V6226","Three Domain Secure AuthStatus is required"),this.errorCodes.set("V6227","Three Domain Secure AuthStatus invalid"),this.errorCodes.set("V6228","Three domain secure Version is required"),this.errorCodes.set("V6230","Three domain secure Directory Server Txn ID invalid"),this.errorCodes.set("V6231","Three domain secure Directory Server Txn ID is required"),this.errorCodes.set("V6232","Three domain secure Version is invalid"),this.errorCodes.set("V6501","Invalid Amex InstallementPlan"),this.errorCodes.set("V6502","Invalid Number Of Installements for Amex. Valid values are from 0 to 99 inclusive"),this.errorCodes.set("V6503","Merchant Amex ID required"),this.errorCodes.set("V6504","Invalid Merchant Amex ID"),this.errorCodes.set("V6505","Merchant Terminal ID required"),this.errorCodes.set("V6506","Merchant category code required"),this.errorCodes.set("V6507","Invalid merchant category code"),this.errorCodes.set("V6508","Amex 3D ECI required"),this.errorCodes.set("V6509","Invalid Amex 3D ECI"),this.errorCodes.set("V6510","Invalid Amex 3D verification value"),this.errorCodes.set("V6511","Invalid merchant location data"),this.errorCodes.set("V6512","Invalid merchant street address"),this.errorCodes.set("V6513","Invalid merchant city"),this.errorCodes.set("V6514","Invalid merchant country"),this.errorCodes.set("V6515","Invalid merchant phone"),this.errorCodes.set("V6516","Invalid merchant postcode"),this.errorCodes.set("V6517","Amex connection error"),this.errorCodes.set("V6518","Amex EC Card Details API returned invalid data"),this.errorCodes.set("V6520","Invalid or missing Amex Point Of Sale Data"),this.errorCodes.set("V6521","Invalid or missing Amex transaction date time"),this.errorCodes.set("V6522","Invalid or missing Amex Original transaction date time"),this.errorCodes.set("V6530","Credit Card Number in non Credit Card Field")}get groupFieldConfig(){var e,r,s,t,o;return{publicApiKey:(e=document.querySelector("meta[name=public-api-key]"))==null?void 0:e.content,fieldDivId:"eway-secure-panel",fieldType:"group",styles:"",layout:{fonts:["Lobster"],rows:[{styles:"",cells:[{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(r=document.querySelector("meta[name=translation-card-name]"))==null?void 0:r.content,styles:""},field:{fieldColSpan:8,fieldType:"name",styles:this.cardStyles,divStyles:"padding-left: 10px;"}},{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(s=document.querySelector("meta[name=translation-expiry_date]"))==null?void 0:s.content,styles:""},field:{fieldColSpan:8,fieldType:"expirytext",styles:this.cardStyles,divStyles:"padding-left: 10px;"}}]},{styles:"",cells:[{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(t=document.querySelector("meta[name=translation-card_number]"))==null?void 0:t.content,styles:""},field:{fieldColSpan:8,fieldType:"card",styles:this.cardStyles}},{colSpan:12,styles:"margin-top: 15px;",label:{fieldColSpan:4,text:(o=document.querySelector("meta[name=translation-cvv]"))==null?void 0:o.content,styles:""},field:{fieldColSpan:8,fieldType:"cvn",styles:this.cardStyles}}]}]}}}securePanelCallback(e){if(document.getElementById("errors").hidden=!0,e.errors)return this.handleErrors(e.errors);document.getElementById("authorize-card")&&(document.getElementById("authorize-card").disabled=!1),document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!1),document.querySelector("input[name=securefieldcode]").value=e.secureFieldCode}handleErrors(e){let r=e.split(" "),s="";r.forEach(t=>{s=s.concat(this.errorCodes.get(t)+"
")}),document.getElementById("errors").innerHTML=s,document.getElementById("errors").hidden=!1}completeAuthorization(e){e.target.parentElement.disabled=!0;const r=document.getElementById("authorize-card");r.querySelector("svg").classList.remove("hidden"),r.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}completePaymentUsingToken(e){e.target.parentElement.disabled=!0,document.getElementById("server-response").submit()}completePaymentWithoutToken(e){e.target.parentElement.disabled=!0;let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.getElementById("server-response").submit()}initialize(){this.eWAY=eWAY.setupSecureField(this.groupFieldConfig,e=>this.securePanelCallback(e))}handle(){var r,s;this.initialize(),(r=document.getElementById("authorize-card"))==null||r.addEventListener("click",t=>this.completeAuthorization(t)),Array.from(document.getElementsByClassName("toggle-payment-with-token")??[]).forEach(t=>t.addEventListener("click",o=>{document.getElementById("eway-secure-panel").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=o.target.dataset.token,document.getElementById("pay-now").disabled=!1})),document.getElementById("toggle-payment-with-credit-card")&&document.getElementById("toggle-payment-with-credit-card").addEventListener("click",t=>{document.getElementById("eway-secure-panel").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",document.getElementById("pay-now").disabled=!0});const e=document.getElementById("pay-now");(s=document.getElementById("pay-now"))==null||s.addEventListener("click",t=>{let o=document.querySelector("input[name=token]");return e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),o.value?this.completePaymentUsingToken(t):this.completePaymentWithoutToken(t)})}}function i(){new a().handle()}d()?i():n("#eway-credit-card-payment").then(()=>i()); diff --git a/public/build/assets/forte-ach-payment-2f7fa236.js b/public/build/assets/forte-ach-payment-546428ee.js similarity index 57% rename from public/build/assets/forte-ach-payment-2f7fa236.js rename to public/build/assets/forte-ach-payment-546428ee.js index faf0f66ed4a8..d4542c93061e 100644 --- a/public/build/assets/forte-ach-payment-2f7fa236.js +++ b/public/build/assets/forte-ach-payment-546428ee.js @@ -1,4 +1,4 @@ -var a=Object.defineProperty;var s=(t,e,n)=>e in t?a(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var o=(t,e,n)=>(s(t,typeof e!="symbol"?e+"":e,n),n);/** +var s=Object.defineProperty;var d=(n,e,t)=>e in n?s(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var o=(n,e,t)=>(d(n,typeof e!="symbol"?e+"":e,t),t);import{i,w as u}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var a=Object.defineProperty;var s=(t,e,n)=>e in t?a(t,e,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://opensource.org/licenses/AAL - */class d{constructor(e){o(this,"handleAuthorization",()=>{var e=document.getElementById("account-number").value,n=document.getElementById("routing-number").value,r={api_login_id:this.apiLoginId,account_number:e,routing_number:n,account_type:"checking"};return document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),forte.createToken(r).success(this.successResponseHandler).error(this.failedResponseHandler),!1});o(this,"successResponseHandler",e=>(document.getElementById("payment_token").value=e.onetime_token,document.getElementById("server_response").submit(),!1));o(this,"failedResponseHandler",e=>{var n='
";return document.getElementById("forte_errors").innerHTML=n,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),!1});o(this,"handle",()=>{let e=document.getElementById("pay-now");return e&&e.addEventListener("click",n=>{this.handleAuthorization()}),this});this.apiLoginId=e}}const u=document.querySelector('meta[name="forte-api-login-id"]').content;new d(u).handle(); + */class c{constructor(e){o(this,"handleAuthorization",()=>{var e=document.getElementById("account-number").value,t=document.getElementById("routing-number").value,r={api_login_id:this.apiLoginId,account_number:e,routing_number:t,account_type:"checking"};return document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),forte.createToken(r).success(this.successResponseHandler).error(this.failedResponseHandler),!1});o(this,"successResponseHandler",e=>(document.getElementById("payment_token").value=e.onetime_token,document.getElementById("server_response").submit(),!1));o(this,"failedResponseHandler",e=>{var t='
";return document.getElementById("forte_errors").innerHTML=t,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),!1});o(this,"handle",()=>{let e=document.getElementById("pay-now");return e&&e.addEventListener("click",t=>{this.handleAuthorization()}),this});this.apiLoginId=e}}function a(){const n=document.querySelector('meta[name="forte-api-login-id"]').content;new c(n).handle()}i()?a():u("#force-ach-payment").then(()=>a()); diff --git a/public/build/assets/forte-credit-card-payment-7bb15431.js b/public/build/assets/forte-credit-card-payment-7bb15431.js deleted file mode 100644 index e57074c187e8..000000000000 --- a/public/build/assets/forte-credit-card-payment-7bb15431.js +++ /dev/null @@ -1,9 +0,0 @@ -var o=Object.defineProperty;var l=(n,e,t)=>e in n?o(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var d=(n,e,t)=>(l(n,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://opensource.org/licenses/AAL - */class c{constructor(e){d(this,"handleAuthorization",()=>{var e=$("#my-card"),t={api_login_id:this.apiLoginId,card_number:e.CardJs("cardNumber").replace(/[^\d]/g,""),expire_year:e.CardJs("expiryYear").replace(/[^\d]/g,""),expire_month:e.CardJs("expiryMonth").replace(/[^\d]/g,""),cvv:document.getElementById("cvv").value.replace(/[^\d]/g,"")};return document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),forte.createToken(t).success(this.successResponseHandler).error(this.failedResponseHandler),!1});d(this,"successResponseHandler",e=>{document.getElementById("payment_token").value=e.onetime_token,document.getElementById("card_brand").value=e.card_brand,document.getElementById("expire_year").value=e.expire_year,document.getElementById("expire_month").value=e.expire_month,document.getElementById("last_4").value=e.last_4;let t=document.querySelector("input[name=token-billing-checkbox]:checked");return t&&(document.getElementById("store_card").value=t.value),document.getElementById("server_response").submit(),!1});d(this,"failedResponseHandler",e=>{var t='
";return document.getElementById("forte_errors").innerHTML=t,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),!1});d(this,"handle",()=>{Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(r=>r.addEventListener("click",a=>{document.getElementById("save-card--container").style.display="none",document.getElementById("forte--credit-card-container").style.display="none",document.getElementById("token").value=a.target.dataset.token}));let e=document.getElementById("toggle-payment-with-credit-card");e&&e.addEventListener("click",()=>{document.getElementById("save-card--container").style.display="grid",document.getElementById("forte--credit-card-container").style.display="flex",document.getElementById("token").value=null});let t=document.getElementById("pay-now");return t&&t.addEventListener("click",r=>{let a=document.getElementById("token");a.value?this.handlePayNowAction(a.value):this.handleAuthorization()}),this});this.apiLoginId=e,this.cardHolderName=document.getElementById("cardholder_name")}handlePayNowAction(e){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),document.getElementById("token").value=e,document.getElementById("server_response").submit()}}const i=document.querySelector('meta[name="forte-api-login-id"]').content;new c(i).handle(); diff --git a/public/build/assets/forte-credit-card-payment-ea7d8632.js b/public/build/assets/forte-credit-card-payment-ea7d8632.js new file mode 100644 index 000000000000..8ea0882de32d --- /dev/null +++ b/public/build/assets/forte-credit-card-payment-ea7d8632.js @@ -0,0 +1,9 @@ +var c=Object.defineProperty;var l=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var a=(n,e,t)=>(l(n,typeof e!="symbol"?e+"":e,t),t);import{i as u,w as m}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://opensource.org/licenses/AAL + */class p{constructor(e){a(this,"handleAuthorization",()=>{var r,d,o,s;const e={api_login_id:this.apiLoginId,card_number:(r=this.sc.value("number"))==null?void 0:r.replace(/[^\d]/g,""),expire_year:`20${(d=this.sc.value("year"))==null?void 0:d.replace(/[^\d]/g,"")}`,expire_month:(o=this.sc.value("month"))==null?void 0:o.replace(/[^\d]/g,""),cvv:(s=this.sc.value("cvv"))==null?void 0:s.replace(/[^\d]/g,"")};return document.getElementById("pay-now")&&(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden")),forte.createToken(e).success(this.successResponseHandler).error(this.failedResponseHandler),!1});a(this,"successResponseHandler",e=>(document.getElementById("payment_token").value=e.onetime_token,document.getElementById("card_brand").value=e.card_type,document.getElementById("server_response").submit(),!1));a(this,"failedResponseHandler",e=>{var t='
";return document.getElementById("forte_errors").innerHTML=t,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),!1});a(this,"handle",()=>{let e=document.getElementById("pay-now");return e&&e.addEventListener("click",t=>{this.handleAuthorization()}),this});this.apiLoginId=e,this.cardHolderName=document.getElementById("cardholder_name"),this.sc=createSimpleCard({fields:{card:{number:"#number",date:"#date",cvv:"#cvv"}}}),this.sc.mount()}}function i(){const n=document.querySelector('meta[name="forte-api-login-id"]').content;new p(n).handle()}u()?i():m("#forte-credit-card-payment").then(()=>i()); diff --git a/public/build/assets/mollie-credit-card-db5c26c6.js b/public/build/assets/mollie-credit-card-d81afbd4.js similarity index 92% rename from public/build/assets/mollie-credit-card-db5c26c6.js rename to public/build/assets/mollie-credit-card-d81afbd4.js index c822f500bafa..6ff5a9fa2dfa 100644 --- a/public/build/assets/mollie-credit-card-db5c26c6.js +++ b/public/build/assets/mollie-credit-card-d81afbd4.js @@ -1,4 +1,4 @@ -/** +import{i as d,w as l}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com). * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class a{constructor(){var e,t;this.mollie=Mollie((e=document.querySelector("meta[name=mollie-profileId]"))==null?void 0:e.content,{testmode:(t=document.querySelector("meta[name=mollie-testmode]"))==null?void 0:t.content,locale:"en_US"})}createCardHolderInput(){let e=this.mollie.createComponent("cardHolder");e.mount("#card-holder");let t=document.getElementById("card-holder-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}createCardNumberInput(){let e=this.mollie.createComponent("cardNumber");e.mount("#card-number");let t=document.getElementById("card-number-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}createExpiryDateInput(){let e=this.mollie.createComponent("expiryDate");e.mount("#expiry-date");let t=document.getElementById("expiry-date-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}createCvvInput(){let e=this.mollie.createComponent("verificationCode");e.mount("#cvv");let t=document.getElementById("cvv-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}handlePayNowButton(){if(document.getElementById("pay-now").disabled=!0,document.querySelector("input[name=token]").value!=="")return document.querySelector("input[name=gateway_response]").value="",document.getElementById("server-response").submit();this.mollie.createToken().then(function(e){let t=e.token,n=e.error;if(n){document.getElementById("pay-now").disabled=!1;let o=document.getElementById("errors");o.innerText=n.message,o.hidden=!1;return}let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.querySelector("input[name=gateway_response]").value=t,document.querySelector("input[name=token]").value="",document.getElementById("server-response").submit()})}handle(){this.createCardHolderInput().createCardNumberInput().createExpiryDateInput().createCvvInput(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.getElementById("mollie--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",e=>{document.getElementById("mollie--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("pay-now").addEventListener("click",()=>this.handlePayNowButton())}}new a().handle(); + */class c{constructor(){var e,t;this.mollie=Mollie((e=document.querySelector("meta[name=mollie-profileId]"))==null?void 0:e.content,{testmode:(t=document.querySelector("meta[name=mollie-testmode]"))==null?void 0:t.content,locale:"en_US"})}createCardHolderInput(){let e=this.mollie.createComponent("cardHolder");e.mount("#card-holder");let t=document.getElementById("card-holder-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}createCardNumberInput(){let e=this.mollie.createComponent("cardNumber");e.mount("#card-number");let t=document.getElementById("card-number-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}createExpiryDateInput(){let e=this.mollie.createComponent("expiryDate");e.mount("#expiry-date");let t=document.getElementById("expiry-date-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}createCvvInput(){let e=this.mollie.createComponent("verificationCode");e.mount("#cvv");let t=document.getElementById("cvv-error");return e.addEventListener("change",function(n){n.error&&n.touched?t.textContent=n.error:t.textContent=""}),this}handlePayNowButton(){if(document.getElementById("pay-now").disabled=!0,document.querySelector("input[name=token]").value!=="")return document.querySelector("input[name=gateway_response]").value="",document.getElementById("server-response").submit();this.mollie.createToken().then(function(e){let t=e.token,n=e.error;if(n){document.getElementById("pay-now").disabled=!1;let o=document.getElementById("errors");o.innerText=n.message,o.hidden=!1;return}let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.querySelector("input[name=gateway_response]").value=t,document.querySelector("input[name=token]").value="",document.getElementById("server-response").submit()})}handle(){this.createCardHolderInput().createCardNumberInput().createExpiryDateInput().createCvvInput(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.getElementById("mollie--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",e=>{document.getElementById("mollie--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("pay-now").addEventListener("click",()=>this.handlePayNowButton())}}function a(){new c().handle()}d()?a():l("#mollie-credit-card-payment").then(()=>a()); diff --git a/public/build/assets/paytrace-credit-card-9cea3700.js b/public/build/assets/paytrace-credit-card-9cea3700.js deleted file mode 100644 index 00c406bb51f2..000000000000 --- a/public/build/assets/paytrace-credit-card-9cea3700.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com). - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class r{constructor(){var t;this.clientKey=(t=document.querySelector("meta[name=paytrace-client-key]"))==null?void 0:t.content}get creditCardStyles(){return{font_color:"#000",border_color:"#a1b1c9",border_style:"dotted",font_size:"13pt",input_border_radius:"3px",input_border_width:"1px",input_font:"Times New Roman, arial, fantasy",input_font_weight:"400",input_margin:"5px 0px 5px 0px",input_padding:"0px 5px 0px 5px",label_color:"#a0aec0",label_size:"16px",label_width:"150px",label_font:"Times New Roman, sans-serif, serif",label_font_weight:"light",label_margin:"5px 0px 0px 0px",label_padding:"0px 5px 0px 5px",background_color:"white",height:"30px",width:"370px",padding_bottom:"0px"}}get codeStyles(){return{font_color:"#000",border_color:"#a1b1c9",border_style:"dotted",font_size:"13pt",input_border_radius:"2px",input_border_width:"1px",input_font:"serif, cursive, fantasy",input_font_weight:"700",input_margin:"5px 0px 5px 20px",input_padding:"0px 5px 0px 5px",label_color:"#a0aec0",label_size:"16px",label_width:"150px",label_font:"sans-serif, arial, serif",label_font_weight:"bold",label_margin:"5px 0px 0px 20px",label_padding:"2px 5px 2px 5px",background_color:"white",height:"30px",width:"150px",padding_bottom:"2px"}}get expStyles(){return{font_color:"#000",border_color:"#a1b1c9",border_style:"dashed",font_size:"12pt",input_border_radius:"0px",input_border_width:"2px",input_font:"arial, cursive, fantasy",input_font_weight:"400",input_margin:"5px 0px 5px 0px",input_padding:"0px 5px 0px 5px",label_color:"#a0aec0",label_size:"16px",label_width:"150px",label_font:"arial, fantasy, serif",label_font_weight:"normal",label_margin:"5px 0px 0px 0px",label_padding:"2px 5px 2px 5px",background_color:"white",height:"30px",width:"85px",padding_bottom:"2px",type:"dropdown"}}updatePayTraceLabels(){window.PTPayment.getControl("securityCode").label.text(document.querySelector("meta[name=ctrans-cvv]").content),window.PTPayment.getControl("creditCard").label.text(document.querySelector("meta[name=ctrans-card_number]").content),window.PTPayment.getControl("expiration").label.text(document.querySelector("meta[name=ctrans-expires]").content)}setupPayTrace(){return window.PTPayment.setup({styles:{code:this.codeStyles,cc:this.creditCardStyles,exp:this.expStyles},authorization:{clientKey:this.clientKey}})}handlePaymentWithCreditCard(t){t.target.parentElement.disabled=!0,document.getElementById("errors").hidden=!0,window.PTPayment.validate(n=>{if(n.length>=1){let e=document.getElementById("errors");return e.textContent=n[0].description,e.hidden=!1,t.target.parentElement.disabled=!1}this.ptInstance.process().then(e=>{document.getElementById("HPF_Token").value=e.message.hpf_token,document.getElementById("enc_key").value=e.message.enc_key;let a=document.querySelector('input[name="token-billing-checkbox"]:checked');a&&(document.querySelector('input[name="store_card"]').value=a.value),document.getElementById("server_response").submit()}).catch(e=>{document.getElementById("errors").textContent=JSON.stringify(e),document.getElementById("errors").hidden=!1,console.log(e)})})}handlePaymentWithToken(t){t.target.parentElement.disabled=!0,document.getElementById("server_response").submit()}handle(){var t;Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(n=>n.addEventListener("click",e=>{document.getElementById("paytrace--credit-card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=e.target.dataset.token})),(t=document.getElementById("toggle-payment-with-credit-card"))==null||t.addEventListener("click",n=>{document.getElementById("paytrace--credit-card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",this.setupPayTrace().then(e=>{this.ptInstance=e,this.updatePayTraceLabels()})}),document.getElementById("pay-now").addEventListener("click",n=>document.querySelector("input[name=token]").value===""?this.handlePaymentWithCreditCard(n):this.handlePaymentWithToken(n))}}new r().handle(); diff --git a/public/build/assets/paytrace-credit-card-d29797c1.js b/public/build/assets/paytrace-credit-card-d29797c1.js new file mode 100644 index 000000000000..7052cba6938d --- /dev/null +++ b/public/build/assets/paytrace-credit-card-d29797c1.js @@ -0,0 +1,9 @@ +import{i as o,w as i}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class l{constructor(){var t;this.clientKey=(t=document.querySelector("meta[name=paytrace-client-key]"))==null?void 0:t.content}get creditCardStyles(){return{font_color:"#000",border_color:"#a1b1c9",border_style:"dotted",font_size:"13pt",input_border_radius:"3px",input_border_width:"1px",input_font:"Times New Roman, arial, fantasy",input_font_weight:"400",input_margin:"5px 0px 5px 0px",input_padding:"0px 5px 0px 5px",label_color:"#a0aec0",label_size:"16px",label_width:"150px",label_font:"Times New Roman, sans-serif, serif",label_font_weight:"light",label_margin:"5px 0px 0px 0px",label_padding:"0px 5px 0px 5px",background_color:"white",height:"30px",width:"370px",padding_bottom:"0px"}}get codeStyles(){return{font_color:"#000",border_color:"#a1b1c9",border_style:"dotted",font_size:"13pt",input_border_radius:"2px",input_border_width:"1px",input_font:"serif, cursive, fantasy",input_font_weight:"700",input_margin:"5px 0px 5px 20px",input_padding:"0px 5px 0px 5px",label_color:"#a0aec0",label_size:"16px",label_width:"150px",label_font:"sans-serif, arial, serif",label_font_weight:"bold",label_margin:"5px 0px 0px 20px",label_padding:"2px 5px 2px 5px",background_color:"white",height:"30px",width:"150px",padding_bottom:"2px"}}get expStyles(){return{font_color:"#000",border_color:"#a1b1c9",border_style:"dashed",font_size:"12pt",input_border_radius:"0px",input_border_width:"2px",input_font:"arial, cursive, fantasy",input_font_weight:"400",input_margin:"5px 0px 5px 0px",input_padding:"0px 5px 0px 5px",label_color:"#a0aec0",label_size:"16px",label_width:"150px",label_font:"arial, fantasy, serif",label_font_weight:"normal",label_margin:"5px 0px 0px 0px",label_padding:"2px 5px 2px 5px",background_color:"white",height:"30px",width:"85px",padding_bottom:"2px",type:"dropdown"}}updatePayTraceLabels(){window.PTPayment.getControl("securityCode").label.text(document.querySelector("meta[name=ctrans-cvv]").content),window.PTPayment.getControl("creditCard").label.text(document.querySelector("meta[name=ctrans-card_number]").content),window.PTPayment.getControl("expiration").label.text(document.querySelector("meta[name=ctrans-expires]").content)}setupPayTrace(){return window.PTPayment.setup({styles:{code:this.codeStyles,cc:this.creditCardStyles,exp:this.expStyles},authorization:{clientKey:this.clientKey}})}handlePaymentWithCreditCard(t){const e=document.getElementById("pay-now");e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),t.target.parentElement.disabled=!0,document.getElementById("errors").hidden=!0,window.PTPayment.validate(a=>{if(a.length>=1){let n=document.getElementById("errors");return n.textContent=a[0].description,n.hidden=!1,e.querySelector("svg").classList.add("hidden"),e.querySelector("span").classList.remove("hidden"),t.target.parentElement.disabled=!1}this.ptInstance.process().then(n=>{document.getElementById("HPF_Token").value=n.message.hpf_token,document.getElementById("enc_key").value=n.message.enc_key;let r=document.querySelector('input[name="token-billing-checkbox"]:checked');r&&(document.querySelector('input[name="store_card"]').value=r.value),document.getElementById("server_response").submit()}).catch(n=>{document.getElementById("errors").textContent=JSON.stringify(n),document.getElementById("errors").hidden=!1,e.querySelector("svg").classList.add("hidden"),e.querySelector("span").classList.remove("hidden"),console.log(n)})})}handlePaymentWithToken(t){t.target.parentElement.disabled=!0;const e=document.getElementById("pay-now");e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server_response").submit()}handle(){var t;Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",a=>{document.getElementById("paytrace--credit-card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=a.target.dataset.token})),(t=document.getElementById("toggle-payment-with-credit-card"))==null||t.addEventListener("click",e=>{document.getElementById("paytrace--credit-card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="",this.setupPayTrace().then(a=>{this.ptInstance=a,this.updatePayTraceLabels()})}),document.getElementById("pay-now").addEventListener("click",e=>document.querySelector("input[name=token]").value===""?this.handlePaymentWithCreditCard(e):this.handlePaymentWithToken(e)),Array.from(document.getElementsByClassName("toggle-payment-with-token")).length===0&&!o()&&document.getElementById("toggle-payment-with-credit-card").click()}}function d(){new l().handle()}o()?d():i("#paytrace-credit-card-payment").then(()=>d()); diff --git a/public/build/assets/razorpay-aio-3d02ff1d.js b/public/build/assets/razorpay-aio-3d02ff1d.js deleted file mode 100644 index 37774d9fd657..000000000000 --- a/public/build/assets/razorpay-aio-3d02ff1d.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Invoice Ninja (https://invoiceninja.com). - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */var a;let n=JSON.parse((a=document.querySelector("meta[name=razorpay-options]"))==null?void 0:a.content);n.handler=function(e){document.getElementById("razorpay_payment_id").value=e.razorpay_payment_id,document.getElementById("razorpay_signature").value=e.razorpay_signature,document.getElementById("server-response").submit()};n.modal={ondismiss:function(){t.disabled=!1}};let o=new Razorpay(n),t=document.getElementById("pay-now");t.onclick=function(e){t.disabled=!0,o.open()}; diff --git a/public/build/assets/razorpay-aio-f8e8c7f0.js b/public/build/assets/razorpay-aio-f8e8c7f0.js new file mode 100644 index 000000000000..9031d5887707 --- /dev/null +++ b/public/build/assets/razorpay-aio-f8e8c7f0.js @@ -0,0 +1,9 @@ +import{i,w as d}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */function o(){var a;let e=JSON.parse((a=document.querySelector("meta[name=razorpay-options]"))==null?void 0:a.content);e.handler=function(n){document.getElementById("razorpay_payment_id").value=n.razorpay_payment_id,document.getElementById("razorpay_signature").value=n.razorpay_signature,document.getElementById("server-response").submit()},e.modal={ondismiss:function(){t.disabled=!1}};let r=new Razorpay(e),t=document.getElementById("pay-now");t.onclick=function(n){t.disabled=!0,r.open()}}i()?o():d("#razorpay-hosted-payment").then(()=>o()); diff --git a/public/build/assets/square-credit-card-a20464a3.js b/public/build/assets/square-credit-card-2fc5c3fa.js similarity index 94% rename from public/build/assets/square-credit-card-a20464a3.js rename to public/build/assets/square-credit-card-2fc5c3fa.js index 11597eeaf5a8..fdd7554e820d 100644 --- a/public/build/assets/square-credit-card-a20464a3.js +++ b/public/build/assets/square-credit-card-2fc5c3fa.js @@ -1,4 +1,4 @@ -/** +import{i,w as d}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com). * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class c{constructor(){this.appId=document.querySelector("meta[name=square-appId]").content,this.locationId=document.querySelector("meta[name=square-locationId]").content,this.isLoaded=!1}async init(){this.payments=Square.payments(this.appId,this.locationId),this.card=await this.payments.card(),await this.card.attach("#card-container"),this.isLoaded=!0;let e=document.querySelector(".sq-card-iframe-container");e&&e.setAttribute("style","150px !important"),document.querySelector(".toggle-payment-with-token")&&document.getElementById("card-container").classList.add("hidden")}async completePaymentWithoutToken(e){document.getElementById("errors").hidden=!0,e.target.parentElement.disabled=!0;let t=await this.card.tokenize(),o;try{const n={amount:document.querySelector("meta[name=amount]").content,billingContact:JSON.parse(document.querySelector("meta[name=square_contact]").content),currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};o=(await this.payments.verifyBuyer(t.token,n)).token}catch{e.target.parentElement.disabled=!0}if(document.querySelector('input[name="verificationToken"]').value=o,t.status==="OK"){document.getElementById("sourceId").value=t.token;let n=document.querySelector('input[name="token-billing-checkbox"]:checked');return n&&(document.querySelector('input[name="store_card"]').value=n.value),document.getElementById("server_response").submit()}document.getElementById("errors").textContent=t.errors[0].message,document.getElementById("errors").hidden=!1,e.target.parentElement.disabled=!1}async completePaymentUsingToken(e){return e.target.parentElement.disabled=!0,document.getElementById("server_response").submit()}async verifyBuyer(e){const t={amount:document.querySelector("meta[name=amount]").content,billingContact:document.querySelector("meta[name=square_contact]").content,currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};return(await this.payments.verifyBuyer(e,t)).token}async handle(){document.getElementById("payment-list").classList.add("hidden"),await this.init().then(()=>{var e,t,o,n;(e=document.getElementById("authorize-card"))==null||e.addEventListener("click",a=>this.completePaymentWithoutToken(a)),(t=document.getElementById("pay-now"))==null||t.addEventListener("click",a=>document.querySelector("input[name=token]").value?this.completePaymentUsingToken(a):this.completePaymentWithoutToken(a)),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(a=>a.addEventListener("click",async r=>{document.getElementById("card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=r.target.dataset.token})),(o=document.getElementById("toggle-payment-with-credit-card"))==null||o.addEventListener("click",async a=>{document.getElementById("card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("loader").classList.add("hidden"),document.getElementById("payment-list").classList.remove("hidden"),(n=document.getElementById("toggle-payment-with-credit-card"))==null||n.click()})}}new c().handle(); + */class s{constructor(){this.appId=document.querySelector("meta[name=square-appId]").content,this.locationId=document.querySelector("meta[name=square-locationId]").content,this.isLoaded=!1}async init(){this.payments=Square.payments(this.appId,this.locationId),this.card=await this.payments.card(),await this.card.attach("#card-container"),this.isLoaded=!0;let e=document.querySelector(".sq-card-iframe-container");e&&e.setAttribute("style","150px !important"),document.querySelector(".toggle-payment-with-token")&&document.getElementById("card-container").classList.add("hidden")}async completePaymentWithoutToken(e){document.getElementById("errors").hidden=!0,e.target.parentElement.disabled=!0;let t=await this.card.tokenize(),o;try{const n={amount:document.querySelector("meta[name=amount]").content,billingContact:JSON.parse(document.querySelector("meta[name=square_contact]").content),currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};o=(await this.payments.verifyBuyer(t.token,n)).token}catch{e.target.parentElement.disabled=!0}if(document.querySelector('input[name="verificationToken"]').value=o,t.status==="OK"){document.getElementById("sourceId").value=t.token;let n=document.querySelector('input[name="token-billing-checkbox"]:checked');return n&&(document.querySelector('input[name="store_card"]').value=n.value),document.getElementById("server_response").submit()}document.getElementById("errors").textContent=t.errors[0].message,document.getElementById("errors").hidden=!1,e.target.parentElement.disabled=!1}async completePaymentUsingToken(e){return e.target.parentElement.disabled=!0,document.getElementById("server_response").submit()}async verifyBuyer(e){const t={amount:document.querySelector("meta[name=amount]").content,billingContact:document.querySelector("meta[name=square_contact]").content,currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};return(await this.payments.verifyBuyer(e,t)).token}async handle(){document.getElementById("payment-list").classList.add("hidden"),await this.init().then(()=>{var e,t,o,n;(e=document.getElementById("authorize-card"))==null||e.addEventListener("click",a=>this.completePaymentWithoutToken(a)),(t=document.getElementById("pay-now"))==null||t.addEventListener("click",a=>document.querySelector("input[name=token]").value?this.completePaymentUsingToken(a):this.completePaymentWithoutToken(a)),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(a=>a.addEventListener("click",async r=>{document.getElementById("card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=r.target.dataset.token})),(o=document.getElementById("toggle-payment-with-credit-card"))==null||o.addEventListener("click",async a=>{document.getElementById("card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("loader").classList.add("hidden"),document.getElementById("payment-list").classList.remove("hidden"),(n=document.getElementById("toggle-payment-with-credit-card"))==null||n.click()})}}function c(){new s().handle()}i()?c():d("#square-credit-card-payment").then(()=>c()); diff --git a/public/build/assets/stripe-ach-pay-22d14901.js b/public/build/assets/stripe-ach-pay-22d14901.js new file mode 100644 index 000000000000..afc3ee0a621e --- /dev/null +++ b/public/build/assets/stripe-ach-pay-22d14901.js @@ -0,0 +1,9 @@ +import{w as g}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */g("#stripe-ach-payment").then(()=>f());function f(){let d=document.getElementById("pay-now");d&&(Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",n=>{document.querySelector("input[name=source]").value=n.target.dataset.token})),d.addEventListener("click",function(){let e=document.getElementById("pay-now");e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()})),document.getElementById("new-bank").addEventListener("click",e=>{var m,y;if(!document.getElementById("accept-terms").checked){errors.textContent="You must accept the mandate terms prior to making payment.",errors.hidden=!1;return}errors.hidden=!0;let n,t=document.querySelector('meta[name="stripe-publishable-key"]').content,o=(m=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:m.content;o?n=Stripe(t,{stripeAccount:o}):n=Stripe(t);let s=document.getElementById("new-bank");s.disabled=!0,s.querySelector("svg").classList.remove("hidden"),s.querySelector("span").classList.add("hidden"),e.preventDefault();const c=document.getElementById("account-holder-name-field"),r=document.getElementById("email-field"),u=(y=document.querySelector('meta[name="client_secret"]'))==null?void 0:y.content;n.collectBankAccountForPayment({clientSecret:u,params:{payment_method_type:"us_bank_account",payment_method_data:{billing_details:{name:c.value,email:r.value}}},expand:["payment_method"]}).then(({paymentIntent:i,error:l})=>{if(l)console.error(l.message),errors.textContent=l.message,errors.hidden=!1,a();else if(i.status==="requires_payment_method"){errors.textContent="We were unable to process the payment with this account, please try another one.",errors.hidden=!1,a();return}else if(i.status==="requires_confirmation"){let h=document.getElementById("bank_account_response");h.value=JSON.stringify(i),p(n,u)}a()})});function p(e,n){e.confirmUsBankAccountPayment(n).then(({paymentIntent:t,error:o})=>{var s,c;if(console.log(t),o)console.error(o.message);else if(t.status==="requires_payment_method")errors.textContent="We were unable to process the payment with this account, please try another one.",errors.hidden=!1,a();else if(t.status==="processing"){let r=document.getElementById("gateway_response");r.value=JSON.stringify(t),document.getElementById("server-response").submit()}else if(((s=t.next_action)==null?void 0:s.type)==="verify_with_microdeposits"||((c=t.next_action)==null?void 0:c.type)==="requires_source_action"){errors.textContent="You will receive an email with details on how to verify your bank account and process payment.",errors.hidden=!1,document.getElementById("new-bank").style.visibility="hidden";let r=document.getElementById("gateway_response");r.value=JSON.stringify(t),document.getElementById("server-response").submit()}})}function a(){let e=document.getElementById("new-bank");e.disabled=!1,e.querySelector("svg").classList.add("hidden"),e.querySelector("span").classList.remove("hidden")}} diff --git a/public/build/assets/stripe-acss-946fe54a.js b/public/build/assets/stripe-acss-02974bdc.js similarity index 70% rename from public/build/assets/stripe-acss-946fe54a.js rename to public/build/assets/stripe-acss-02974bdc.js index 8b12e28c4d42..8993512f897d 100644 --- a/public/build/assets/stripe-acss-946fe54a.js +++ b/public/build/assets/stripe-acss-02974bdc.js @@ -1,4 +1,4 @@ -var c=Object.defineProperty;var i=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(i(n,typeof e!="symbol"?e+"":e,t),t);/** +var c=Object.defineProperty;var d=(o,e,t)=>e in o?c(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var a=(o,e,t)=>(d(o,typeof e!="symbol"?e+"":e,t),t);import{i,w as m}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var c=Object.defineProperty;var i=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class l{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handle",()=>{Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.querySelector("input[name=token]").value=t.target.dataset.token,console.log(t.target.dataset.token)})),document.getElementById("toggle-payment-with-new-account")&&document.getElementById("toggle-payment-with-new-account").addEventListener("click",e=>{document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("pay-now-with-token")?document.getElementById("pay-now-with-token").addEventListener("click",e=>{document.querySelector("input[name=token]").value,document.getElementById("pay-now-with-token").disabled=!0,document.querySelector("#pay-now-with-token > svg").classList.remove("hidden"),document.querySelector("#pay-now-with-token > span").classList.add("hidden"),document.getElementById("server-response").submit()}):document.getElementById("pay-now").addEventListener("click",e=>{let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value);let o=document.getElementById("errors");if(o.textContent="",o.hidden=!0,document.getElementById("acss-name").value===""){document.getElementById("acss-name").focus(),o.textContent=document.querySelector("meta[name=translation-name-required]").content,o.hidden=!1;return}if(document.getElementById("acss-email-address").value===""){document.getElementById("acss-email-address").focus(),o.textContent=document.querySelector("meta[name=translation-email-required]").content,o.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmAcssDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:document.getElementById("acss-name").value,email:document.getElementById("acss-email-address").value}}}).then(s=>s.error?this.handleFailure(s.error.message):this.handleSuccess(s))})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}var a;const m=((a=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:a.content)??"";var d;const u=((d=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:d.content)??"";new l(m,u).setupStripe().handle(); + */class l{constructor(e,t){a(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));a(this,"handle",()=>{Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.querySelector("input[name=token]").value=t.target.dataset.token,console.log(t.target.dataset.token)})),document.getElementById("toggle-payment-with-new-account")&&document.getElementById("toggle-payment-with-new-account").addEventListener("click",e=>{document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("pay-now-with-token")?document.getElementById("pay-now-with-token").addEventListener("click",e=>{document.querySelector("input[name=token]").value,document.getElementById("pay-now-with-token").disabled=!0,document.querySelector("#pay-now-with-token > svg").classList.remove("hidden"),document.querySelector("#pay-now-with-token > span").classList.add("hidden"),document.getElementById("server-response").submit()}):document.getElementById("pay-now").addEventListener("click",e=>{let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value);let n=document.getElementById("errors");if(n.textContent="",n.hidden=!0,document.getElementById("acss-name").value===""){document.getElementById("acss-name").focus(),n.textContent=document.querySelector("meta[name=translation-name-required]").content,n.hidden=!1;return}if(document.getElementById("acss-email-address").value===""){document.getElementById("acss-email-address").focus(),n.textContent=document.querySelector("meta[name=translation-email-required]").content,n.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmAcssDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:document.getElementById("acss-name").value,email:document.getElementById("acss-email-address").value}}}).then(s=>s.error?this.handleFailure(s.error.message):this.handleSuccess(s))})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}function r(){var n,s;const o=((n=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:n.content)??"",e=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new l(o,e).setupStripe().handle();const t=document.querySelector('input[name="payment-type"]');t&&t.click()}i()?r():m("#stripe-acss-payment").then(()=>r()); diff --git a/public/build/assets/stripe-alipay-00a4a19f.js b/public/build/assets/stripe-alipay-1457b63d.js similarity index 69% rename from public/build/assets/stripe-alipay-00a4a19f.js rename to public/build/assets/stripe-alipay-1457b63d.js index 1aa7a164b732..6e1ebe2832ec 100644 --- a/public/build/assets/stripe-alipay-00a4a19f.js +++ b/public/build/assets/stripe-alipay-1457b63d.js @@ -1,9 +1,9 @@ -var i=Object.defineProperty;var c=(n,e,t)=>e in n?i(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(c(n,typeof e!="symbol"?e+"":e,t),t);/** +var i=Object.defineProperty;var c=(n,e,t)=>e in n?i(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(c(n,typeof e!="symbol"?e+"":e,t),t);import{i as a,w as d}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license - */class a{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));this.key=e,this.stripeConnect=t,this.errors=document.getElementById("errors")}async handle(){document.getElementById("pay-now").addEventListener("click",async e=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");const{error:t}=await this.stripe.confirmAlipayPayment(document.querySelector("meta[name=ci_intent]").content,{return_url:`${document.querySelector("meta[name=return_url]").content}`});document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),t&&(this.errors.textContent="",this.errors.textContent=result.error.message,this.errors.hidden=!1)})}}var s;const d=((s=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:s.content)??"";var o;const l=((o=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:o.content)??"";new a(d,l).setupStripe().handle(); + * @license https://www.elastic.co/licensing/elastic-license + */class l{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));this.key=e,this.stripeConnect=t,this.errors=document.getElementById("errors")}async handle(){document.getElementById("pay-now").addEventListener("click",async e=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");const{error:t}=await this.stripe.confirmAlipayPayment(document.querySelector("meta[name=ci_intent]").content,{return_url:`${document.querySelector("meta[name=return_url]").content}`});document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),t&&(this.errors.textContent="",this.errors.textContent=result.error.message,this.errors.hidden=!1)})}}function o(){var t,s;const n=((t=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:t.content)??"",e=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new l(n,e).setupStripe().handle()}a()?o():d("#stripe-alipay-payment").then(()=>o()); diff --git a/public/build/assets/stripe-bacs-38c8b975.js b/public/build/assets/stripe-bacs-38c8b975.js deleted file mode 100644 index d71314edc9a3..000000000000 --- a/public/build/assets/stripe-bacs-38c8b975.js +++ /dev/null @@ -1,9 +0,0 @@ -var a=Object.defineProperty;var c=(n,e,t)=>e in n?a(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var s=(n,e,t)=>(c(n,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class d{constructor(e,t){s(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));s(this,"payment_data");s(this,"handle",()=>{this.onlyAuthorization?document.getElementById("authorize-bacs").addEventListener("click",e=>{document.getElementById("authorize-bacs").disabled=!0,document.querySelector("#authorize-bacs > svg").classList.remove("hidden"),document.querySelector("#authorize-bacs > span").classList.add("hidden"),location.href=document.querySelector("meta[name=stripe-redirect-url]").content}):(this.payNowButton=document.getElementById("pay-now"),document.getElementById("pay-now").addEventListener("click",e=>{this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}),this.payment_data=Array.from(document.getElementsByClassName("toggle-payment-with-token")),this.payment_data.length>0?this.payment_data.forEach(e=>e.addEventListener("click",t=>{document.querySelector("input[name=token]").value=t.target.dataset.token})):(this.errors.textContent=document.querySelector("meta[name=translation-payment-method-required]").content,this.errors.hidden=!1,this.payNowButton.disabled=!0,this.payNowButton.querySelector("span").classList.remove("hidden"),this.payNowButton.querySelector("svg").classList.add("hidden")))});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t,this.onlyAuthorization=h}}var o;const u=((o=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:o.content)??"";var r;const l=((r=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:r.content)??"";var i;const h=((i=document.querySelector('meta[name="only-authorization"]'))==null?void 0:i.content)??"";new d(u,l).setupStripe().handle(); diff --git a/public/build/assets/stripe-bacs-eff96e18.js b/public/build/assets/stripe-bacs-eff96e18.js new file mode 100644 index 000000000000..059b82b8c290 --- /dev/null +++ b/public/build/assets/stripe-bacs-eff96e18.js @@ -0,0 +1,9 @@ +var d=Object.defineProperty;var u=(n,e,t)=>e in n?d(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var o=(n,e,t)=>(u(n,typeof e!="symbol"?e+"":e,t),t);import{i as l,w as m}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class h{constructor(e,t,s){o(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));o(this,"payment_data");o(this,"handle",()=>{this.onlyAuthorization?document.getElementById("authorize-bacs").addEventListener("click",e=>{document.getElementById("authorize-bacs").disabled=!0,document.querySelector("#authorize-bacs > svg").classList.remove("hidden"),document.querySelector("#authorize-bacs > span").classList.add("hidden"),location.href=document.querySelector("meta[name=stripe-redirect-url]").content}):(this.payNowButton=document.getElementById("pay-now"),document.getElementById("pay-now").addEventListener("click",e=>{this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}),this.payment_data=Array.from(document.getElementsByClassName("toggle-payment-with-token")),this.payment_data.length>0?this.payment_data.forEach(e=>e.addEventListener("click",t=>{document.querySelector("input[name=token]").value=t.target.dataset.token})):(this.errors.textContent=document.querySelector("meta[name=translation-payment-method-required]").content,this.errors.hidden=!1,this.payNowButton.disabled=!0,this.payNowButton.querySelector("span").classList.remove("hidden"),this.payNowButton.querySelector("svg").classList.add("hidden")))});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t,this.onlyAuthorization=s}}function c(){var i,r,a;const n=((i=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:i.content)??"",e=((r=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:r.content)??"",t=((a=document.querySelector('meta[name="only-authorization"]'))==null?void 0:a.content)??"";new h(n,e,t).setupStripe().handle();const s=document.querySelector('input[name="payment-type"]');s&&s.click()}l()?c():m("#stripe-bacs-payment").then(()=>c()); diff --git a/public/build/assets/stripe-bancontact-4a0d7a40.js b/public/build/assets/stripe-bancontact-4a0d7a40.js new file mode 100644 index 000000000000..bb49bc04a6b9 --- /dev/null +++ b/public/build/assets/stripe-bancontact-4a0d7a40.js @@ -0,0 +1,9 @@ +var s=Object.defineProperty;var a=(n,t,e)=>t in n?s(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var r=(n,t,e)=>(a(n,typeof t!="symbol"?t+"":t,e),e);import{i,w as m}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class l{constructor(t,e){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(!document.getElementById("bancontact-name").value){e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1,console.log("name");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmBancontactPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:document.getElementById("bancontact-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}}function c(){var e,o;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((o=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:o.content)??"";new l(n,t).setupStripe().handle()}i()?c():m("#stripe-bancontact-payment").then(()=>c()); diff --git a/public/build/assets/stripe-bancontact-cb004d43.js b/public/build/assets/stripe-bancontact-cb004d43.js deleted file mode 100644 index 5545f86111b7..000000000000 --- a/public/build/assets/stripe-bancontact-cb004d43.js +++ /dev/null @@ -1,9 +0,0 @@ -var s=Object.defineProperty;var a=(n,e,t)=>e in n?s(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(a(n,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class i{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{let t=document.getElementById("errors");if(!document.getElementById("bancontact-name").value){t.textContent=document.querySelector("meta[name=translation-name-required]").content,t.hidden=!1,console.log("name");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmBancontactPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:document.getElementById("bancontact-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}var o;const m=((o=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:o.content)??"";var c;const l=((c=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:c.content)??"";new i(m,l).setupStripe().handle(); diff --git a/public/build/assets/stripe-bank-transfer-4ab58b35.js b/public/build/assets/stripe-bank-transfer-4ab58b35.js new file mode 100644 index 000000000000..c723f15b55f7 --- /dev/null +++ b/public/build/assets/stripe-bank-transfer-4ab58b35.js @@ -0,0 +1,9 @@ +import{i as p,w as y}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */p()?c():y("#stripe-bank-transfer-payment").then(()=>c());function c(){var r,o,s;const m=(r=document.querySelector('meta[name="stripe-client-secret"]'))==null?void 0:r.content,i=(o=document.querySelector('meta[name="stripe-return-url"]'))==null?void 0:o.content,d={clientSecret:m,appearance:{theme:"stripe",variables:{colorPrimary:"#0570de",colorBackground:"#ffffff",colorText:"#30313d",colorDanger:"#df1b41",fontFamily:"Ideal Sans, system-ui, sans-serif",spacingUnit:"2px",borderRadius:"4px"}}},e=Stripe(document.querySelector('meta[name="stripe-publishable-key"]').getAttribute("content")),t=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";t&&(e.stripeAccount=t);const n=e.elements(d);n.create("payment").mount("#payment-element"),document.getElementById("payment-form").addEventListener("submit",async l=>{l.preventDefault(),document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden");const{error:a}=await e.confirmPayment({elements:n,confirmParams:{return_url:i}});if(a){document.getElementById("pay-now").disabled=!1,document.querySelector("svg").classList.remove("hidden"),document.querySelector("span").classList.add("hidden");const u=document.querySelector("#errors");u.textContent=a.message}})} diff --git a/public/build/assets/stripe-becs-4d1494ed.js b/public/build/assets/stripe-becs-483b1b23.js similarity index 82% rename from public/build/assets/stripe-becs-4d1494ed.js rename to public/build/assets/stripe-becs-483b1b23.js index 9dede5b9728a..25f64659c544 100644 --- a/public/build/assets/stripe-becs-4d1494ed.js +++ b/public/build/assets/stripe-becs-483b1b23.js @@ -1,4 +1,4 @@ -var r=Object.defineProperty;var d=(n,t,e)=>t in n?r(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var o=(n,t,e)=>(d(n,typeof t!="symbol"?t+"":t,e),e);/** +var c=Object.defineProperty;var r=(n,t,e)=>t in n?c(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var o=(n,t,e)=>(r(n,typeof t!="symbol"?t+"":t,e),e);import{i,w as d}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var r=Object.defineProperty;var d=(n,t,e)=>t in n?r(n,t,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class i{constructor(t,e){o(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);const t=this.stripe.elements(),s={style:{base:{color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"},":-webkit-autofill":{color:"#32325d"}},invalid:{color:"#fa755a",iconColor:"#fa755a",":-webkit-autofill":{color:"#fa755a"}}},disabled:!1,hideIcon:!1,iconStyle:"default"};return this.auBankAccount=t.create("auBankAccount",s),this.auBankAccount.mount("#becs-iban"),this});o(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(document.getElementById("becs-name").value===""){document.getElementById("becs-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}if(document.getElementById("becs-email-address").value===""){document.getElementById("becs-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,e.hidden=!1;return}if(!document.getElementById("becs-mandate-acceptance").checked){document.getElementById("becs-mandate-acceptance").focus(),e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1,console.log("Terms");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmAuBecsDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{au_becs_debit:this.auBankAccount,billing_details:{name:document.getElementById("becs-name").value,email:document.getElementById("becs-email-address").value}}}).then(s=>s.error?this.handleFailure(s.error.message):this.handleSuccess(s))})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}handleSuccess(t){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(t.paymentIntent),document.getElementById("server-response").submit()}handleFailure(t){let e=document.getElementById("errors");e.textContent="",e.textContent=t,e.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}var a;const l=((a=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:a.content)??"";var c;const m=((c=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:c.content)??"";new i(l,m).setupStripe().handle(); + */class l{constructor(t,e){o(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);const t=this.stripe.elements(),s={style:{base:{color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"},":-webkit-autofill":{color:"#32325d"}},invalid:{color:"#fa755a",iconColor:"#fa755a",":-webkit-autofill":{color:"#fa755a"}}},disabled:!1,hideIcon:!1,iconStyle:"default"};return this.auBankAccount=t.create("auBankAccount",s),this.auBankAccount.mount("#becs-iban"),this});o(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(document.getElementById("becs-name").value===""){document.getElementById("becs-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}if(document.getElementById("becs-email-address").value===""){document.getElementById("becs-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,e.hidden=!1;return}if(!document.getElementById("becs-mandate-acceptance").checked){document.getElementById("becs-mandate-acceptance").focus(),e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1,console.log("Terms");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmAuBecsDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{au_becs_debit:this.auBankAccount,billing_details:{name:document.getElementById("becs-name").value,email:document.getElementById("becs-email-address").value}}}).then(s=>s.error?this.handleFailure(s.error.message):this.handleSuccess(s))})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}handleSuccess(t){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(t.paymentIntent),document.getElementById("server-response").submit()}handleFailure(t){let e=document.getElementById("errors");e.textContent="",e.textContent=t,e.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}function a(){var e,s;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new l(n,t).setupStripe().handle()}i()?a():d("#stripe-becs-payment").then(()=>a()); diff --git a/public/build/assets/stripe-browserpay-ac78fb26.js b/public/build/assets/stripe-browserpay-c23582f0.js similarity index 90% rename from public/build/assets/stripe-browserpay-ac78fb26.js rename to public/build/assets/stripe-browserpay-c23582f0.js index 5433bd793132..f0952ba0e348 100644 --- a/public/build/assets/stripe-browserpay-ac78fb26.js +++ b/public/build/assets/stripe-browserpay-c23582f0.js @@ -1,4 +1,4 @@ -/** +import{i as o,w as i}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com). * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class a{constructor(){var e;this.clientSecret=(e=document.querySelector("meta[name=stripe-pi-client-secret]"))==null?void 0:e.content}init(){var t,n;let e={};return document.querySelector("meta[name=stripe-account-id]")&&(e.apiVersion="2020-08-27",e.stripeAccount=(t=document.querySelector("meta[name=stripe-account-id]"))==null?void 0:t.content),this.stripe=Stripe((n=document.querySelector("meta[name=stripe-publishable-key]"))==null?void 0:n.content,e),this.elements=this.stripe.elements(),this}createPaymentRequest(){return this.paymentRequest=this.stripe.paymentRequest(JSON.parse(document.querySelector("meta[name=payment-request-data").content)),this}createPaymentRequestButton(){this.paymentRequestButton=this.elements.create("paymentRequestButton",{paymentRequest:this.paymentRequest})}handlePaymentRequestEvents(e,t){document.querySelector("#errors").hidden=!0,this.paymentRequest.on("paymentmethod",function(n){e.confirmCardPayment(t,{payment_method:n.paymentMethod.id},{handleActions:!1}).then(function(r){r.error?(document.querySelector("#errors").innerText=r.error.message,document.querySelector("#errors").hidden=!1,n.complete("fail")):(n.complete("success"),r.paymentIntent.status==="requires_action"?e.confirmCardPayment(t).then(function(s){s.error?(n.complete("fail"),document.querySelector("#errors").innerText=s.error.message,document.querySelector("#errors").hidden=!1):(document.querySelector('input[name="gateway_response"]').value=JSON.stringify(s.paymentIntent),document.getElementById("server-response").submit())}):(document.querySelector('input[name="gateway_response"]').value=JSON.stringify(r.paymentIntent),document.getElementById("server-response").submit()))})})}handle(){this.init().createPaymentRequest().createPaymentRequestButton(),this.paymentRequest.canMakePayment().then(e=>{var t;if(e)return this.paymentRequestButton.mount("#payment-request-button");document.querySelector("#errors").innerHTML=JSON.parse((t=document.querySelector("meta[name=no-available-methods]"))==null?void 0:t.content),document.querySelector("#errors").hidden=!1}),this.handlePaymentRequestEvents(this.stripe,this.clientSecret)}}new a().handle(); + */class m{constructor(){var e;this.clientSecret=(e=document.querySelector("meta[name=stripe-pi-client-secret]"))==null?void 0:e.content}init(){var t,n;let e={};return document.querySelector("meta[name=stripe-account-id]")&&(e.apiVersion="2020-08-27",e.stripeAccount=(t=document.querySelector("meta[name=stripe-account-id]"))==null?void 0:t.content),this.stripe=Stripe((n=document.querySelector("meta[name=stripe-publishable-key]"))==null?void 0:n.content,e),this.elements=this.stripe.elements(),this}createPaymentRequest(){return this.paymentRequest=this.stripe.paymentRequest(JSON.parse(document.querySelector("meta[name=payment-request-data").content)),this}createPaymentRequestButton(){this.paymentRequestButton=this.elements.create("paymentRequestButton",{paymentRequest:this.paymentRequest})}handlePaymentRequestEvents(e,t){document.querySelector("#errors").hidden=!0,this.paymentRequest.on("paymentmethod",function(n){e.confirmCardPayment(t,{payment_method:n.paymentMethod.id},{handleActions:!1}).then(function(r){r.error?(document.querySelector("#errors").innerText=r.error.message,document.querySelector("#errors").hidden=!1,n.complete("fail")):(n.complete("success"),r.paymentIntent.status==="requires_action"?e.confirmCardPayment(t).then(function(s){s.error?(n.complete("fail"),document.querySelector("#errors").innerText=s.error.message,document.querySelector("#errors").hidden=!1):(document.querySelector('input[name="gateway_response"]').value=JSON.stringify(s.paymentIntent),document.getElementById("server-response").submit())}):(document.querySelector('input[name="gateway_response"]').value=JSON.stringify(r.paymentIntent),document.getElementById("server-response").submit()))})})}handle(){this.init().createPaymentRequest().createPaymentRequestButton(),this.paymentRequest.canMakePayment().then(e=>{var t;if(e)return this.paymentRequestButton.mount("#payment-request-button");document.querySelector("#errors").innerHTML=JSON.parse((t=document.querySelector("meta[name=no-available-methods]"))==null?void 0:t.content),document.querySelector("#errors").hidden=!1}),this.handlePaymentRequestEvents(this.stripe,this.clientSecret)}}function a(){new m().handle()}o()?a():i("#stripe-browserpay-payment").then(()=>a()); diff --git a/public/build/assets/stripe-credit-card-75322a3b.js b/public/build/assets/stripe-credit-card-5487be17.js similarity index 82% rename from public/build/assets/stripe-credit-card-75322a3b.js rename to public/build/assets/stripe-credit-card-5487be17.js index c0ed12095051..ffbeb08332ea 100644 --- a/public/build/assets/stripe-credit-card-75322a3b.js +++ b/public/build/assets/stripe-credit-card-5487be17.js @@ -1,9 +1,9 @@ -/** +import{i as c,w as u}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license - */class l{constructor(e,t,n,d){this.key=e,this.secret=t,this.onlyAuthorization=n,this.stripeConnect=d}setupStripe(){return this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this.elements=this.stripe.elements(),this}createElement(){var e;return this.cardElement=this.elements.create("card",{hidePostalCode:((e=document.querySelector("meta[name=stripe-require-postal-code]"))==null?void 0:e.content)==="0",value:{postalCode:document.querySelector("meta[name=client-postal-code]").content},hideIcon:!1}),this}mountCardElement(){return this.cardElement.mount("#card-element"),this}completePaymentUsingToken(){let e=document.querySelector("input[name=token]").value,t=document.getElementById("pay-now");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardPayment(this.secret,{payment_method:e}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}completePaymentWithoutToken(){let e=document.getElementById("pay-now");this.payNowButton=e,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden");let t=document.getElementById("cardholder-name");this.stripe.handleCardPayment(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:t.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,this.payNowButton.disabled=!1,this.payNowButton.querySelector("svg").classList.add("hidden"),this.payNowButton.querySelector("span").classList.remove("hidden")}handleAuthorization(){let e=document.getElementById("cardholder-name"),t=document.getElementById("authorize-card");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardSetup(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:e.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccessfulAuthorization(n))}handleSuccessfulAuthorization(e){document.getElementById("gateway_response").value=JSON.stringify(e.setupIntent),document.getElementById("server_response").submit()}handle(){this.setupStripe(),this.onlyAuthorization?(this.createElement().mountCardElement(),document.getElementById("authorize-card").addEventListener("click",()=>this.handleAuthorization())):(Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.getElementById("stripe--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",e=>{document.getElementById("stripe--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),this.createElement().mountCardElement(),document.getElementById("pay-now").addEventListener("click",()=>{try{return document.querySelector("input[name=token]").value?this.completePaymentUsingToken():this.completePaymentWithoutToken()}catch(e){console.log(e.message)}}))}}var o;const c=((o=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:o.content)??"";var r;const u=((r=document.querySelector('meta[name="stripe-secret"]'))==null?void 0:r.content)??"";var a;const m=((a=document.querySelector('meta[name="only-authorization"]'))==null?void 0:a.content)??"";var s;const h=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";let i=new l(c,u,m,h);i.handle();document.addEventListener("livewire:init",()=>{Livewire.on("passed-required-fields-check",()=>i.handle())}); + * @license https://www.elastic.co/licensing/elastic-license + */class m{constructor(e,t,n,r){this.key=e,this.secret=t,this.onlyAuthorization=n,this.stripeConnect=r}setupStripe(){return this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this.elements=this.stripe.elements(),this}createElement(){var e;return this.cardElement=this.elements.create("card",{hidePostalCode:((e=document.querySelector("meta[name=stripe-require-postal-code]"))==null?void 0:e.content)==="0",value:{postalCode:document.querySelector("meta[name=client-postal-code]").content},hideIcon:!1}),this}mountCardElement(){return this.cardElement.mount("#card-element"),this}completePaymentUsingToken(){let e=document.querySelector("input[name=token]").value,t=document.getElementById("pay-now");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardPayment(this.secret,{payment_method:e}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}completePaymentWithoutToken(){let e=document.getElementById("pay-now");this.payNowButton=e,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden");let t=document.getElementById("cardholder-name");this.stripe.handleCardPayment(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:t.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,this.payNowButton.disabled=!1,this.payNowButton.querySelector("svg").classList.add("hidden"),this.payNowButton.querySelector("span").classList.remove("hidden")}handleAuthorization(){let e=document.getElementById("cardholder-name"),t=document.getElementById("authorize-card");this.payNowButton=t,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),this.stripe.handleCardSetup(this.secret,this.cardElement,{payment_method_data:{billing_details:{name:e.value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccessfulAuthorization(n))}handleSuccessfulAuthorization(e){document.getElementById("gateway_response").value=JSON.stringify(e.setupIntent),document.getElementById("server_response").submit()}handle(){this.setupStripe(),this.onlyAuthorization?(this.createElement().mountCardElement(),document.getElementById("authorize-card").addEventListener("click",()=>this.handleAuthorization())):(Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",t=>{document.getElementById("stripe--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",e=>{document.getElementById("stripe--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),this.createElement().mountCardElement(),document.getElementById("pay-now").addEventListener("click",()=>{try{return document.querySelector("input[name=token]").value?this.completePaymentUsingToken():this.completePaymentWithoutToken()}catch(e){console.log(e.message)}}))}}function l(){var a,s,i,d;const o=((a=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:a.content)??"",e=((s=document.querySelector('meta[name="stripe-secret"]'))==null?void 0:s.content)??"",t=((i=document.querySelector('meta[name="only-authorization"]'))==null?void 0:i.content)??"",n=((d=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:d.content)??"";new m(o,e,t,n).handle()}c()?l():u("#stripe-credit-card-payment").then(()=>l()); diff --git a/public/build/assets/stripe-eps-0c461508.js b/public/build/assets/stripe-eps-0c461508.js new file mode 100644 index 000000000000..e6670bf79fac --- /dev/null +++ b/public/build/assets/stripe-eps-0c461508.js @@ -0,0 +1,9 @@ +var i=Object.defineProperty;var a=(n,t,e)=>t in n?i(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var s=(n,t,e)=>(a(n,typeof t!="symbol"?t+"":t,e),e);import{i as c,w as l}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class m{constructor(t,e){s(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let t=this.stripe.elements();var e={style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"}}}};return this.eps=t.create("epsBank",e),this.eps.mount("#eps-bank-element"),this});s(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(!document.getElementById("eps-name").value){e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmEpsPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{eps:this.eps,billing_details:{name:document.getElementById("eps-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}}function o(){var e,r;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((r=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:r.content)??"";new m(n,t).setupStripe().handle()}c()?o():l("#stripe-eps-payment").then(()=>o()); diff --git a/public/build/assets/stripe-eps-6ebc87cd.js b/public/build/assets/stripe-eps-6ebc87cd.js deleted file mode 100644 index 5ca0fc192c8f..000000000000 --- a/public/build/assets/stripe-eps-6ebc87cd.js +++ /dev/null @@ -1,9 +0,0 @@ -var i=Object.defineProperty;var c=(n,e,t)=>e in n?i(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var s=(n,e,t)=>(c(n,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class a{constructor(e,t){s(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let e=this.stripe.elements();var t={style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"}}}};return this.eps=e.create("epsBank",t),this.eps.mount("#eps-bank-element"),this});s(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{let t=document.getElementById("errors");if(!document.getElementById("eps-name").value){t.textContent=document.querySelector("meta[name=translation-name-required]").content,t.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmEpsPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{eps:this.eps,billing_details:{name:document.getElementById("eps-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}var r;const l=((r=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:r.content)??"";var o;const m=((o=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:o.content)??"";new a(l,m).setupStripe().handle(); diff --git a/public/build/assets/stripe-fpx-240a05e2.js b/public/build/assets/stripe-fpx-c82fd7dc.js similarity index 72% rename from public/build/assets/stripe-fpx-240a05e2.js rename to public/build/assets/stripe-fpx-c82fd7dc.js index 5cf0503f394c..f3a37d114281 100644 --- a/public/build/assets/stripe-fpx-240a05e2.js +++ b/public/build/assets/stripe-fpx-c82fd7dc.js @@ -1,4 +1,4 @@ -var i=Object.defineProperty;var c=(n,t,e)=>t in n?i(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var s=(n,t,e)=>(c(n,typeof t!="symbol"?t+"":t,e),e);/** +var i=Object.defineProperty;var a=(n,t,e)=>t in n?i(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var s=(n,t,e)=>(a(n,typeof t!="symbol"?t+"":t,e),e);import{i as c,w as d}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var i=Object.defineProperty;var c=(n,t,e)=>t in n?i(n,t,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class d{constructor(t,e){s(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let t=this.stripe.elements(),e={base:{padding:"10px 12px",color:"#32325d",fontSize:"16px"}};return this.fpx=t.create("fpxBank",{style:e,accountHolderType:"individual"}),this.fpx.mount("#fpx-bank-element"),this});s(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmFpxPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{fpx:this.fpx},return_url:document.querySelector('meta[name="return-url"]').content}).then(e=>{e.error&&this.handleFailure(e.error.message)})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}handleFailure(t){let e=document.getElementById("errors");e.textContent="",e.textContent=t,e.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}var r;const a=((r=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:r.content)??"";var o;const l=((o=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:o.content)??"";new d(a,l).setupStripe().handle(); + */class l{constructor(t,e){s(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let t=this.stripe.elements(),e={base:{padding:"10px 12px",color:"#32325d",fontSize:"16px"}};return this.fpx=t.create("fpxBank",{style:e,accountHolderType:"individual"}),this.fpx.mount("#fpx-bank-element"),this});s(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmFpxPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{fpx:this.fpx},return_url:document.querySelector('meta[name="return-url"]').content}).then(e=>{e.error&&this.handleFailure(e.error.message)})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}handleFailure(t){let e=document.getElementById("errors");e.textContent="",e.textContent=t,e.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}function o(){var e,r;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((r=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:r.content)??"";new l(n,t).setupStripe().handle()}c()?o():d("#stripe-fpx-payment").then(()=>o()); diff --git a/public/build/assets/stripe-giropay-9d3bfbab.js b/public/build/assets/stripe-giropay-9d3bfbab.js deleted file mode 100644 index 5440e284156e..000000000000 --- a/public/build/assets/stripe-giropay-9d3bfbab.js +++ /dev/null @@ -1,9 +0,0 @@ -var c=Object.defineProperty;var i=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(i(n,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class a{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{let t=document.getElementById("errors");if(!document.getElementById("giropay-mandate-acceptance").checked){t.textContent=document.querySelector("meta[name=translation-terms-required]").content,t.hidden=!1,console.log("Terms");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmGiropayPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:document.getElementById("giropay-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}var o;const d=((o=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:o.content)??"";var s;const m=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new a(d,m).setupStripe().handle(); diff --git a/public/build/assets/stripe-giropay-aedb4a64.js b/public/build/assets/stripe-giropay-aedb4a64.js new file mode 100644 index 000000000000..e9eb2a9a92b4 --- /dev/null +++ b/public/build/assets/stripe-giropay-aedb4a64.js @@ -0,0 +1,9 @@ +var i=Object.defineProperty;var c=(n,t,e)=>t in n?i(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var r=(n,t,e)=>(c(n,typeof t!="symbol"?t+"":t,e),e);import{i as a,w as m}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class d{constructor(t,e){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(!document.getElementById("giropay-mandate-acceptance").checked){e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1,console.log("Terms");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmGiropayPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:document.getElementById("giropay-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}}function s(){var e,o;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((o=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:o.content)??"";new d(n,t).setupStripe().handle()}a()?s():m("#stripe-giropay-payment").then(()=>s()); diff --git a/public/build/assets/stripe-ideal-2110e54f.js b/public/build/assets/stripe-ideal-2110e54f.js new file mode 100644 index 000000000000..140dce58769b --- /dev/null +++ b/public/build/assets/stripe-ideal-2110e54f.js @@ -0,0 +1,9 @@ +var l=Object.defineProperty;var c=(n,t,e)=>t in n?l(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var r=(n,t,e)=>(c(n,typeof t!="symbol"?t+"":t,e),e);import{i as a,w as o}from"./wait-8f4ae121.js";/** + * Invoice Ninja (https://invoiceninja.com) + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */class d{constructor(t,e){r(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let t=this.stripe.elements();var e={style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"}}}};return this.ideal=t.create("idealBank",e),this.ideal.mount("#ideal-bank-element"),this});r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(!document.getElementById("ideal-name").value){e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1,console.log("name");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmIdealPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{ideal:this.ideal,billing_details:{name:document.getElementById("ideal-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}}function i(){var e,s;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new d(n,t).setupStripe().handle()}a()?i():o("#stripe-ideal-payment").then(()=>i());a()?i():o("#stripe-ideal-payment").then(()=>i()); diff --git a/public/build/assets/stripe-ideal-efa175e9.js b/public/build/assets/stripe-ideal-efa175e9.js deleted file mode 100644 index fed5af0ad5ac..000000000000 --- a/public/build/assets/stripe-ideal-efa175e9.js +++ /dev/null @@ -1,9 +0,0 @@ -var o=Object.defineProperty;var a=(n,e,t)=>e in n?o(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(a(n,typeof e!="symbol"?e+"":e,t),t);/** - * Invoice Ninja (https://invoiceninja.com) - * - * @link https://github.com/invoiceninja/invoiceninja source repository - * - * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) - * - * @license https://www.elastic.co/licensing/elastic-license - */class l{constructor(e,t){r(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let e=this.stripe.elements();var t={style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"}}}};return this.ideal=e.create("idealBank",t),this.ideal.mount("#ideal-bank-element"),this});r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{let t=document.getElementById("errors");if(!document.getElementById("ideal-name").value){t.textContent=document.querySelector("meta[name=translation-name-required]").content,t.hidden=!1,console.log("name");return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmIdealPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{ideal:this.ideal,billing_details:{name:document.getElementById("ideal-name").value}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}var i;const c=((i=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:i.content)??"";var s;const d=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new l(c,d).setupStripe().handle(); diff --git a/public/build/assets/stripe-klarna-e45c946d.js b/public/build/assets/stripe-klarna-0c59275f.js similarity index 60% rename from public/build/assets/stripe-klarna-e45c946d.js rename to public/build/assets/stripe-klarna-0c59275f.js index 9fda752c30f3..67dbaa45e291 100644 --- a/public/build/assets/stripe-klarna-e45c946d.js +++ b/public/build/assets/stripe-klarna-0c59275f.js @@ -1,4 +1,4 @@ -var m=Object.defineProperty;var d=(n,e,t)=>e in n?m(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(d(n,typeof e!="symbol"?e+"":e,t),t);/** +var d=Object.defineProperty;var i=(n,e,t)=>e in n?d(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var o=(n,e,t)=>(i(n,typeof e!="symbol"?e+"":e,t),t);import{i as c,w as m}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var m=Object.defineProperty;var d=(n,e,t)=>e in n?m(n,e,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class l{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handleError",e=>{document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),this.errors.textContent="",this.errors.textContent=e,this.errors.hidden=!1});r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{let t=document.getElementById("errors"),o=document.getElementById("klarna-name").value;/^[A-Za-z\s]*$/.test(o)?(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmKlarnaPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:o,email:document.querySelector("meta[name=email]").content,address:{line1:document.querySelector("meta[name=address-1]").content,line2:document.querySelector("meta[name=address-2]").content,city:document.querySelector("meta[name=city]").content,postal_code:document.querySelector("meta[name=postal_code]").content,state:document.querySelector("meta[name=state]").content,country:document.querySelector("meta[name=country]").content}}},return_url:document.querySelector('meta[name="return-url"]').content}).then(a=>{if(a.hasOwnProperty("error"))return this.handleError(a.error.message)})):(document.getElementById("klarna-name-correction").hidden=!1,document.getElementById("klarna-name").textContent=o.replace(/^[A-Za-z\s]*$/,""),document.getElementById("klarna-name").focus(),t.textContent=document.querySelector("meta[name=translation-name-without-special-characters]").content,t.hidden=!1)})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}var c;const i=((c=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:c.content)??"";var s;const u=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new l(i,u).setupStripe().handle(); + */class l{constructor(e,t){o(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));o(this,"handleError",e=>{document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),this.errors.textContent="",this.errors.textContent=e,this.errors.hidden=!1});o(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{let t=document.getElementById("errors"),r=document.getElementById("klarna-name").value;/^[A-Za-z\s]*$/.test(r)?(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmKlarnaPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{billing_details:{name:r,email:document.querySelector("meta[name=email]").content,address:{line1:document.querySelector("meta[name=address-1]").content,line2:document.querySelector("meta[name=address-2]").content,city:document.querySelector("meta[name=city]").content,postal_code:document.querySelector("meta[name=postal_code]").content,state:document.querySelector("meta[name=state]").content,country:document.querySelector("meta[name=country]").content}}},return_url:document.querySelector('meta[name="return-url"]').content}).then(s=>{if(s.hasOwnProperty("error"))return this.handleError(s.error.message)})):(document.getElementById("klarna-name-correction").hidden=!1,document.getElementById("klarna-name").textContent=r.replace(/^[A-Za-z\s]*$/,""),document.getElementById("klarna-name").focus(),t.textContent=document.querySelector("meta[name=translation-name-without-special-characters]").content,t.hidden=!1)})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}function a(){var t,r;const n=((t=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:t.content)??"",e=((r=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:r.content)??"";new l(n,e).setupStripe().handle()}c()?a():m("#stripe-klarna-payment").then(()=>a());c()?a():m("#stripe-klarna-payment").then(()=>a()); diff --git a/public/build/assets/stripe-przelewy24-f9154acf.js b/public/build/assets/stripe-przelewy24-07696bca.js similarity index 75% rename from public/build/assets/stripe-przelewy24-f9154acf.js rename to public/build/assets/stripe-przelewy24-07696bca.js index 88a19bfa445d..5775ecd675d8 100644 --- a/public/build/assets/stripe-przelewy24-f9154acf.js +++ b/public/build/assets/stripe-przelewy24-07696bca.js @@ -1,4 +1,4 @@ -var d=Object.defineProperty;var s=(n,t,e)=>t in n?d(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var o=(n,t,e)=>(s(n,typeof t!="symbol"?t+"":t,e),e);/** +var d=Object.defineProperty;var m=(n,t,e)=>t in n?d(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var r=(n,t,e)=>(m(n,typeof t!="symbol"?t+"":t,e),e);import{i as s,w as c}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var d=Object.defineProperty;var s=(n,t,e)=>t in n?d(n,t,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class m{constructor(t,e){o(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let t=this.stripe.elements();var e={style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"}}}};return this.p24bank=t.create("p24Bank",e),this.p24bank.mount("#p24-bank-element"),this});o(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(document.getElementById("p24-name").value===""){document.getElementById("p24-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}if(document.getElementById("p24-email-address").value===""){document.getElementById("p24-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,e.hidden=!1;return}if(!document.getElementById("p24-mandate-acceptance").checked){document.getElementById("p24-mandate-acceptance").focus(),e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmP24Payment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{p24:this.p24bank,billing_details:{name:document.getElementById("p24-name").value,email:document.getElementById("p24-email-address").value}},payment_method_options:{p24:{tos_shown_and_accepted:document.getElementById("p24-mandate-acceptance").checked}},return_url:document.querySelector('meta[name="return-url"]').content}).then(function(a){a.error?(e.textContent=a.error.message,e.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")):a.paymentIntent.status==="succeeded"&&(window.location=document.querySelector('meta[name="return-url"]').content)})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}}var r;const i=((r=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:r.content)??"";var c;const l=((c=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:c.content)??"";new m(i,l).setupStripe().handle(); + */class i{constructor(t,e){r(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);let t=this.stripe.elements();var e={style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px","::placeholder":{color:"#aab7c4"}}}};return this.p24bank=t.create("p24Bank",e),this.p24bank.mount("#p24-bank-element"),this});r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",t=>{let e=document.getElementById("errors");if(document.getElementById("p24-name").value===""){document.getElementById("p24-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}if(document.getElementById("p24-email-address").value===""){document.getElementById("p24-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,e.hidden=!1;return}if(!document.getElementById("p24-mandate-acceptance").checked){document.getElementById("p24-mandate-acceptance").focus(),e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmP24Payment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{p24:this.p24bank,billing_details:{name:document.getElementById("p24-name").value,email:document.getElementById("p24-email-address").value}},payment_method_options:{p24:{tos_shown_and_accepted:document.getElementById("p24-mandate-acceptance").checked}},return_url:document.querySelector('meta[name="return-url"]').content}).then(function(a){a.error?(e.textContent=a.error.message,e.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")):a.paymentIntent.status==="succeeded"&&(window.location=document.querySelector('meta[name="return-url"]').content)})})});this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=e}}function o(){var e,a;const n=((e=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:e.content)??"",t=((a=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:a.content)??"";new i(n,t).setupStripe().handle()}s()?o():c("#stripe-przelewy24-payment").then(()=>o());s()?o():c("#stripe-przelewy24-payment").then(()=>o()); diff --git a/public/build/assets/stripe-sepa-6dd487fc.js b/public/build/assets/stripe-sepa-23154322.js similarity index 85% rename from public/build/assets/stripe-sepa-6dd487fc.js rename to public/build/assets/stripe-sepa-23154322.js index 525db0c1e7b5..7f05ad082dd7 100644 --- a/public/build/assets/stripe-sepa-6dd487fc.js +++ b/public/build/assets/stripe-sepa-23154322.js @@ -1,4 +1,4 @@ -var s=Object.defineProperty;var l=(a,e,t)=>e in a?s(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var o=(a,e,t)=>(l(a,typeof e!="symbol"?e+"":e,t),t);/** +var i=Object.defineProperty;var l=(a,e,t)=>e in a?i(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var r=(a,e,t)=>(l(a,typeof e!="symbol"?e+"":e,t),t);import{i as s,w as c}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var s=Object.defineProperty;var l=(a,e,t)=>e in a?s(a,e,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class i{constructor(e,t){o(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);const e=this.stripe.elements();var t={base:{color:"#32325d",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',fontSmoothing:"antialiased",fontSize:"16px","::placeholder":{color:"#aab7c4"},":-webkit-autofill":{color:"#32325d"}},invalid:{color:"#fa755a",iconColor:"#fa755a",":-webkit-autofill":{color:"#fa755a"}}},n={style:t,supportedCountries:["SEPA"],placeholderCountry:document.querySelector('meta[name="country"]').content};return this.iban=e.create("iban",n),this.iban.mount("#sepa-iban"),document.getElementById("sepa-name").value=document.querySelector("meta[name=client_name]").content,document.getElementById("sepa-email-address").value=document.querySelector("meta[name=client_email]").content,this});o(this,"handle",()=>{let e=document.getElementById("errors");Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",n=>{document.getElementById("stripe--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token})),document.getElementById("toggle-payment-with-new-bank-account").addEventListener("click",t=>{document.getElementById("stripe--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("pay-now").addEventListener("click",t=>{if(document.querySelector("input[name=token]").value.length!==0)document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:document.querySelector("input[name=token]").value}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n));else{if(document.getElementById("sepa-name").value===""){document.getElementById("sepa-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}if(document.getElementById("sepa-email-address").value===""){document.getElementById("sepa-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,e.hidden=!1;return}if(!document.getElementById("sepa-mandate-acceptance").checked){e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{sepa_debit:this.iban,billing_details:{name:document.getElementById("sepa-name").value,email:document.getElementById("sepa-email-address").value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.querySelector("input[name=token]").value.length>2&&(document.querySelector('input[name="store_card"]').value=!1),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}}var r;const d=((r=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:r.content)??"";var c;const m=((c=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:c.content)??"";new i(d,m).setupStripe().handle(); + */class d{constructor(e,t){r(this,"setupStripe",()=>{this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key);const e=this.stripe.elements();var t={base:{color:"#32325d",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',fontSmoothing:"antialiased",fontSize:"16px","::placeholder":{color:"#aab7c4"},":-webkit-autofill":{color:"#32325d"}},invalid:{color:"#fa755a",iconColor:"#fa755a",":-webkit-autofill":{color:"#fa755a"}}},n={style:t,supportedCountries:["SEPA"],placeholderCountry:document.querySelector('meta[name="country"]').content};return this.iban=e.create("iban",n),this.iban.mount("#sepa-iban"),document.getElementById("sepa-name").value=document.querySelector("meta[name=client_name]").content,document.getElementById("sepa-email-address").value=document.querySelector("meta[name=client_email]").content,this});r(this,"handle",()=>{let e=document.getElementById("errors");Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",n=>{document.getElementById("stripe--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=n.target.dataset.token})),document.getElementById("toggle-payment-with-new-bank-account").addEventListener("click",t=>{document.getElementById("stripe--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("pay-now").addEventListener("click",t=>{if(document.querySelector("input[name=token]").value.length!==0)document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:document.querySelector("input[name=token]").value}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n));else{if(document.getElementById("sepa-name").value===""){document.getElementById("sepa-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,e.hidden=!1;return}if(document.getElementById("sepa-email-address").value===""){document.getElementById("sepa-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,e.hidden=!1;return}if(!document.getElementById("sepa-mandate-acceptance").checked){e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1;return}document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{sepa_debit:this.iban,billing_details:{name:document.getElementById("sepa-name").value,email:document.getElementById("sepa-email-address").value}}}).then(n=>n.error?this.handleFailure(n.error.message):this.handleSuccess(n))}})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.querySelector("input[name=token]").value.length>2&&(document.querySelector('input[name="store_card"]').value=!1),document.getElementById("server-response").submit()}handleFailure(e){let t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}handleSuccess(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);let t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}}function o(){var t,n;const a=((t=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:t.content)??"",e=((n=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:n.content)??"";new d(a,e).setupStripe().handle()}s()?o():c("#stripe-sepa-payment").then(()=>o());s()?o():c("#stripe-sepa-payment").then(()=>o()); diff --git a/public/build/assets/stripe-sofort-18aeca06.js b/public/build/assets/stripe-sofort-92ec46e7.js similarity index 56% rename from public/build/assets/stripe-sofort-18aeca06.js rename to public/build/assets/stripe-sofort-92ec46e7.js index bb1601d347f4..b577c47f8799 100644 --- a/public/build/assets/stripe-sofort-18aeca06.js +++ b/public/build/assets/stripe-sofort-92ec46e7.js @@ -1,4 +1,4 @@ -var c=Object.defineProperty;var i=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var r=(n,e,t)=>(i(n,typeof e!="symbol"?e+"":e,t),t);/** +var a=Object.defineProperty;var u=(n,e,t)=>e in n?a(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var o=(n,e,t)=>(u(n,typeof e!="symbol"?e+"":e,t),t);import{i as c,w as i}from"./wait-8f4ae121.js";/** * Invoice Ninja (https://invoiceninja.com) * * @link https://github.com/invoiceninja/invoiceninja source repository @@ -6,4 +6,4 @@ var c=Object.defineProperty;var i=(n,e,t)=>e in n?c(n,e,{enumerable:!0,configura * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * * @license https://www.elastic.co/licensing/elastic-license - */class u{constructor(e,t){r(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));r(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmSofortPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{sofort:{country:document.querySelector('meta[name="country"]').content}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}var o;const a=((o=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:o.content)??"";var s;const m=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new u(a,m).setupStripe().handle(); + */class m{constructor(e,t){o(this,"setupStripe",()=>(this.stripeConnect?this.stripe=Stripe(this.key,{stripeAccount:this.stripeConnect}):this.stripe=Stripe(this.key),this));o(this,"handle",()=>{document.getElementById("pay-now").addEventListener("click",e=>{document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),this.stripe.confirmSofortPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{sofort:{country:document.querySelector('meta[name="country"]').content}},return_url:document.querySelector('meta[name="return-url"]').content})})});this.key=e,this.errors=document.getElementById("errors"),this.stripeConnect=t}}function r(){var t,s;const n=((t=document.querySelector('meta[name="stripe-publishable-key"]'))==null?void 0:t.content)??"",e=((s=document.querySelector('meta[name="stripe-account-id"]'))==null?void 0:s.content)??"";new m(n,e).setupStripe().handle()}c()?r():i("#stripe-sofort-payment").then(()=>r());c()?r():i("#stripe-sofort-payment").then(()=>r()); diff --git a/public/build/assets/wait-8f4ae121.js b/public/build/assets/wait-8f4ae121.js new file mode 100644 index 000000000000..ec3d03b10652 --- /dev/null +++ b/public/build/assets/wait-8f4ae121.js @@ -0,0 +1,9 @@ +/** + * Invoice Ninja (https://invoiceninja.com). + * + * @link https://github.com/invoiceninja/invoiceninja source repository + * + * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) + * + * @license https://www.elastic.co/licensing/elastic-license + */function i(...e){return new Promise(n=>{if(!e.length){n([]);return}const r=e.map(t=>document.querySelector(t)).filter(Boolean);if(r.length===e.length){n(r);return}const o=new MutationObserver(()=>{const t=e.map(u=>document.querySelector(u)).filter(Boolean);t.length===e.length&&(o.disconnect(),n(t))});o.observe(document.body,{childList:!0,subtree:!0})})}function a(){const e=document.querySelector('meta[name="instant-payment"]');return!!(e&&e instanceof HTMLMetaElement&&e.content==="yes")}export{a as i,i as w}; diff --git a/public/build/manifest.json b/public/build/manifest.json index f1747c8d7f50..07e7f3bd97d8 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -8,6 +8,9 @@ "__commonjsHelpers-725317a4.js" ] }, + "_wait-8f4ae121.js": { + "file": "assets/wait-8f4ae121.js" + }, "resources/js/app.js": { "file": "assets/app-e0713224.js", "imports": [ @@ -36,7 +39,7 @@ "src": "resources/js/clients/linkify-urls.js" }, "resources/js/clients/payment_methods/authorize-authorize-card.js": { - "file": "assets/authorize-authorize-card-7da1185c.js", + "file": "assets/authorize-authorize-card-39be6d93.js", "isEntry": true, "src": "resources/js/clients/payment_methods/authorize-authorize-card.js" }, @@ -45,8 +48,19 @@ "isEntry": true, "src": "resources/js/clients/payment_methods/authorize-checkout-card.js" }, + "resources/js/clients/payment_methods/authorize-stripe-acss.js": { + "file": "assets/authorize-stripe-acss-f6bd46c1.js", + "imports": [ + "_wait-8f4ae121.js" + ], + "isEntry": true, + "src": "resources/js/clients/payment_methods/authorize-stripe-acss.js" + }, "resources/js/clients/payment_methods/braintree-ach.js": { - "file": "assets/braintree-ach-b29d040e.js", + "file": "assets/braintree-ach-3d6b328f.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payment_methods/braintree-ach.js" }, @@ -56,137 +70,231 @@ "src": "resources/js/clients/payment_methods/wepay-bank-account.js" }, "resources/js/clients/payments/authorize-credit-card-payment.js": { - "file": "assets/authorize-credit-card-payment-a217579b.js", + "file": "assets/authorize-credit-card-payment-222655bd.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/authorize-credit-card-payment.js" }, "resources/js/clients/payments/braintree-credit-card.js": { - "file": "assets/braintree-credit-card-1c3f3108.js", + "file": "assets/braintree-credit-card-60bd8878.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/braintree-credit-card.js" }, "resources/js/clients/payments/braintree-paypal.js": { - "file": "assets/braintree-paypal-45391805.js", + "file": "assets/braintree-paypal-f78ad64b.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/braintree-paypal.js" }, "resources/js/clients/payments/checkout-credit-card.js": { - "file": "assets/checkout-credit-card-8a04938c.js", + "file": "assets/checkout-credit-card-2cca8b36.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/checkout-credit-card.js" }, "resources/js/clients/payments/eway-credit-card.js": { - "file": "assets/eway-credit-card-62ce5f3b.js", + "file": "assets/eway-credit-card-150298fa.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/eway-credit-card.js" }, "resources/js/clients/payments/forte-ach-payment.js": { - "file": "assets/forte-ach-payment-2f7fa236.js", + "file": "assets/forte-ach-payment-546428ee.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/forte-ach-payment.js" }, "resources/js/clients/payments/forte-credit-card-payment.js": { - "file": "assets/forte-credit-card-payment-7bb15431.js", + "file": "assets/forte-credit-card-payment-ea7d8632.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/forte-credit-card-payment.js" }, "resources/js/clients/payments/mollie-credit-card.js": { - "file": "assets/mollie-credit-card-db5c26c6.js", + "file": "assets/mollie-credit-card-d81afbd4.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/mollie-credit-card.js" }, "resources/js/clients/payments/paytrace-credit-card.js": { - "file": "assets/paytrace-credit-card-9cea3700.js", + "file": "assets/paytrace-credit-card-d29797c1.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/paytrace-credit-card.js" }, "resources/js/clients/payments/razorpay-aio.js": { - "file": "assets/razorpay-aio-3d02ff1d.js", + "file": "assets/razorpay-aio-f8e8c7f0.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/razorpay-aio.js" }, "resources/js/clients/payments/square-credit-card.js": { - "file": "assets/square-credit-card-a20464a3.js", + "file": "assets/square-credit-card-2fc5c3fa.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/square-credit-card.js" }, + "resources/js/clients/payments/stripe-ach-pay.js": { + "file": "assets/stripe-ach-pay-22d14901.js", + "imports": [ + "_wait-8f4ae121.js" + ], + "isEntry": true, + "src": "resources/js/clients/payments/stripe-ach-pay.js" + }, "resources/js/clients/payments/stripe-ach.js": { "file": "assets/stripe-ach-fe366ca7.js", "isEntry": true, "src": "resources/js/clients/payments/stripe-ach.js" }, "resources/js/clients/payments/stripe-acss.js": { - "file": "assets/stripe-acss-946fe54a.js", + "file": "assets/stripe-acss-02974bdc.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-acss.js" }, "resources/js/clients/payments/stripe-alipay.js": { - "file": "assets/stripe-alipay-00a4a19f.js", + "file": "assets/stripe-alipay-1457b63d.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-alipay.js" }, "resources/js/clients/payments/stripe-bacs.js": { - "file": "assets/stripe-bacs-38c8b975.js", + "file": "assets/stripe-bacs-eff96e18.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-bacs.js" }, "resources/js/clients/payments/stripe-bancontact.js": { - "file": "assets/stripe-bancontact-cb004d43.js", + "file": "assets/stripe-bancontact-4a0d7a40.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-bancontact.js" }, + "resources/js/clients/payments/stripe-bank-transfer.js": { + "file": "assets/stripe-bank-transfer-4ab58b35.js", + "imports": [ + "_wait-8f4ae121.js" + ], + "isEntry": true, + "src": "resources/js/clients/payments/stripe-bank-transfer.js" + }, "resources/js/clients/payments/stripe-becs.js": { - "file": "assets/stripe-becs-4d1494ed.js", + "file": "assets/stripe-becs-483b1b23.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-becs.js" }, "resources/js/clients/payments/stripe-browserpay.js": { - "file": "assets/stripe-browserpay-ac78fb26.js", + "file": "assets/stripe-browserpay-c23582f0.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-browserpay.js" }, "resources/js/clients/payments/stripe-credit-card.js": { - "file": "assets/stripe-credit-card-75322a3b.js", + "file": "assets/stripe-credit-card-5487be17.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-credit-card.js" }, "resources/js/clients/payments/stripe-eps.js": { - "file": "assets/stripe-eps-6ebc87cd.js", + "file": "assets/stripe-eps-0c461508.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-eps.js" }, "resources/js/clients/payments/stripe-fpx.js": { - "file": "assets/stripe-fpx-240a05e2.js", + "file": "assets/stripe-fpx-c82fd7dc.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-fpx.js" }, "resources/js/clients/payments/stripe-giropay.js": { - "file": "assets/stripe-giropay-9d3bfbab.js", + "file": "assets/stripe-giropay-aedb4a64.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-giropay.js" }, "resources/js/clients/payments/stripe-ideal.js": { - "file": "assets/stripe-ideal-efa175e9.js", + "file": "assets/stripe-ideal-2110e54f.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-ideal.js" }, "resources/js/clients/payments/stripe-klarna.js": { - "file": "assets/stripe-klarna-e45c946d.js", + "file": "assets/stripe-klarna-0c59275f.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-klarna.js" }, "resources/js/clients/payments/stripe-przelewy24.js": { - "file": "assets/stripe-przelewy24-f9154acf.js", + "file": "assets/stripe-przelewy24-07696bca.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-przelewy24.js" }, "resources/js/clients/payments/stripe-sepa.js": { - "file": "assets/stripe-sepa-6dd487fc.js", + "file": "assets/stripe-sepa-23154322.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-sepa.js" }, "resources/js/clients/payments/stripe-sofort.js": { - "file": "assets/stripe-sofort-18aeca06.js", + "file": "assets/stripe-sofort-92ec46e7.js", + "imports": [ + "_wait-8f4ae121.js" + ], "isEntry": true, "src": "resources/js/clients/payments/stripe-sofort.js" }, @@ -240,7 +348,7 @@ "src": "resources/js/setup/setup.js" }, "resources/sass/app.scss": { - "file": "assets/app-fee1da41.css", + "file": "assets/app-23f93261.css", "isEntry": true, "src": "resources/sass/app.scss" } diff --git a/public/vendor/simple-card@0.0.4/simple-card.js b/public/vendor/simple-card@0.0.4/simple-card.js new file mode 100644 index 000000000000..402945d5f520 --- /dev/null +++ b/public/vendor/simple-card@0.0.4/simple-card.js @@ -0,0 +1,4 @@ +var SimpleCard=function(m){"use strict";var Y=m=>{throw TypeError(m)};var $e=(m,v,w)=>v.has(m)||Y("Cannot "+w);var X=(m,v,w)=>v.has(m)?Y("Cannot add the same private member more than once"):v instanceof WeakSet?v.add(m):v.set(m,w);var J=(m,v,w)=>($e(m,v,"access private method"),w);var L,Q;const v={mask:/^.*$/,preprocessors:[],postprocessors:[],plugins:[],overwriteMode:"shift"};class w{constructor(){this.now=null,this.past=[],this.future=[]}undo(){const e=this.past.pop();e&&this.now&&(this.future.push(this.now),this.updateElement(e,"historyUndo"))}redo(){const e=this.future.pop();e&&this.now&&(this.past.push(this.now),this.updateElement(e,"historyRedo"))}updateHistory(e){if(!this.now){this.now=e;return}const s=this.now.value!==e.value,t=this.now.selection.some((i,r)=>i!==e.selection[r]);!s&&!t||(s&&(this.past.push(this.now),this.future=[]),this.now=e)}updateElement(e,s){this.now=e,this.updateElementState(e,{inputType:s,data:null})}}function ee(n,...e){return e.every(({value:s})=>s===n.value)}function te(n,...e){return e.every(({value:s,selection:t})=>s===n.value&&t[0]===n.selection[0]&&t[1]===n.selection[1])}function ne({value:n,selection:e},s,t){const[i,r]=e,l=typeof t=="function"?t({value:n,selection:e}):t;return{value:n,selection:l==="replace"?[i,i+s.length]:[i,r]}}function T(n){return typeof n=="string"}function j(n,e,s,t){let i="";for(let r=e.length;r{const i=e[t];return T(i)?s===i:s.match(i)}):e.test(n)}function se(n,e,s){let t=null,i=null;const r=Array.from(n.value).reduce((o,a,c)=>{const u=j(e,o,a,s),d=o+u,h=e[d.length];return T(h)?d+h:a.match(h)?(t===null&&c>=n.selection[0]&&(t=d.length),i===null&&c>=n.selection[1]&&(i=d.length),d+a):d},""),l=j(e,r,"",s);return{value:$(r+l,e)?r+l:r,selection:[t??r.length,i??r.length]}}function ie({value:n,selection:e},s){const[t,i]=e;let r=t,l=i;return{value:Array.from(n).reduce((a,c,u)=>{const d=a+c;return t===u&&(r=a.length),i===u&&(l=a.length),d.match(s)?d:a},""),selection:[r,l]}}function C(n,e,s=null){if($(n.value,e))return n;const{value:t,selection:i}=Array.isArray(e)?se(n,e,s):ie(n,e);return{selection:i,value:Array.isArray(e)?t.slice(0,e.length):t}}function H(n,e){if(!Array.isArray(e))return n;const[s,t]=n.selection,i=[],r=Array.from(n.value).reduce((l,o,a)=>{const c=e[a];return a===s&&i.push(l.length),a===t&&i.push(l.length),T(c)&&c===o?l:l+o},"");return i.length<2&&i.push(...new Array(2-i.length).fill(r.length)),{value:r,selection:[i[0],i[1]]}}class M{constructor(e,s){this.initialElementState=e,this.maskOptions=s,this.value="",this.selection=[0,0];const{value:t,selection:i}=C(this.initialElementState,this.getMaskExpression(this.initialElementState));this.value=t,this.selection=i}addCharacters([e,s],t){const{value:i}=this,r=this.getMaskExpression({value:i.slice(0,e)+t+i.slice(s),selection:[e+t.length,e+t.length]}),l={value:i,selection:[e,s]},o=H(l,r),[a,c]=ne(o,t,this.maskOptions.overwriteMode).selection,u=o.value.slice(0,a)+t,d=u.length,h=C({value:u+o.value.slice(c),selection:[d,d]},r,l);if(i.slice(0,a)===C({value:u,selection:[d,d]},r,l).value||te(this,h))throw new Error("Invalid mask value");this.value=h.value,this.selection=h.selection}deleteCharacters([e,s]){if(e===s||!s)return;const{value:t}=this,i=this.getMaskExpression({value:t.slice(0,e)+t.slice(s),selection:[e,e]}),r={value:t,selection:[e,s]},l=H(r,i),[o,a]=l.selection,c=l.value.slice(0,o)+l.value.slice(a),u=C({value:c,selection:[o,o]},i,r);this.value=u.value,this.selection=u.selection}getMaskExpression(e){const{mask:s}=this.maskOptions;return typeof s=="function"?s(e):s}}class re{constructor(e){this.element=e,this.listeners=[]}listen(e,s,t){const i=s;this.element.addEventListener(e,i,t),this.listeners.push(()=>this.element.removeEventListener(e,i))}destroy(){this.listeners.forEach(e=>e())}}const g={CTRL:1,ALT:2,SHIFT:4,META:8},b={Y:89,Z:90};function D(n,e,s){return n.ctrlKey===!!(e&g.CTRL)&&n.altKey===!!(e&g.ALT)&&n.shiftKey===!!(e&g.SHIFT)&&n.metaKey===!!(e&g.META)&&n.keyCode===s}function le(n){return D(n,g.CTRL,b.Y)||D(n,g.CTRL|g.SHIFT,b.Z)||D(n,g.META|g.SHIFT,b.Z)}function ae(n){return D(n,g.CTRL,b.Z)||D(n,g.META,b.Z)}function oe({value:n,selection:e},s){const[t,i]=e;if(t!==i)return[t,i];const r=s?n.slice(t).indexOf(` +`)+1||n.length:n.slice(0,i).lastIndexOf(` +`)+1;return[s?t:r,s?r:i]}function ce({value:n,selection:e},s){const[t,i]=e;return t!==i?[t,i]:(s?[t,i+1]:[t-1,i]).map(l=>Math.min(Math.max(l,0),n.length))}const ue=/\s+$/g,de=/^\s+/g,V=/\s/;function he({value:n,selection:e},s){const[t,i]=e;if(t!==i)return[t,i];if(s){const a=n.slice(t),[c]=a.match(de)||[""],u=a.trimStart().search(V);return[t,u!==-1?t+c.length+u:n.length]}const r=n.slice(0,i),[l]=r.match(ue)||[""],o=r.trimEnd().split("").reverse().findIndex(a=>a.match(V));return[o!==-1?i-l.length-o:0,i]}function x(n=[]){return(e,...s)=>n.reduce((t,i)=>Object.assign(Object.assign({},t),i(t,...s)),e)}function fe(n,e){const s=Object.assign(Object.assign({},v),e),t=x(s.preprocessors),i=x(s.postprocessors),r=typeof n=="string"?{value:n,selection:[0,0]}:n,{elementState:l}=t({elementState:r,data:""},"validation"),o=new M(l,s),{value:a,selection:c}=i(o,r);return typeof n=="string"?a:{value:a,selection:c}}class I extends w{constructor(e,s){super(),this.element=e,this.maskitoOptions=s,this.isTextArea=this.element.nodeName==="TEXTAREA",this.eventListener=new re(this.element),this.options=Object.assign(Object.assign({},v),this.maskitoOptions),this.preprocessor=x(this.options.preprocessors),this.postprocessor=x(this.options.postprocessors),this.teardowns=this.options.plugins.map(t=>t(this.element,this.options)),this.updateHistory(this.elementState),this.eventListener.listen("keydown",t=>{if(le(t))return t.preventDefault(),this.redo();if(ae(t))return t.preventDefault(),this.undo()}),this.eventListener.listen("beforeinput",t=>{var i;const r=t.inputType.includes("Forward");switch(this.updateHistory(this.elementState),t.inputType){case"historyUndo":return t.preventDefault(),this.undo();case"historyRedo":return t.preventDefault(),this.redo();case"deleteByCut":case"deleteContentBackward":case"deleteContentForward":return this.handleDelete({event:t,isForward:r,selection:ce(this.elementState,r)});case"deleteWordForward":case"deleteWordBackward":return this.handleDelete({event:t,isForward:r,selection:he(this.elementState,r),force:!0});case"deleteSoftLineBackward":case"deleteSoftLineForward":case"deleteHardLineBackward":case"deleteHardLineForward":return this.handleDelete({event:t,isForward:r,selection:oe(this.elementState,r),force:!0});case"insertCompositionText":return;case"insertReplacementText":return;case"insertLineBreak":case"insertParagraph":return this.handleEnter(t);case"insertFromPaste":case"insertText":case"insertFromDrop":default:return this.handleInsert(t,t.data||((i=t.dataTransfer)===null||i===void 0?void 0:i.getData("text/plain"))||"")}}),this.eventListener.listen("input",({inputType:t})=>{t!=="insertCompositionText"&&(this.ensureValueFitsMask(),this.updateHistory(this.elementState))}),this.eventListener.listen("compositionend",()=>{this.ensureValueFitsMask(),this.updateHistory(this.elementState)})}get elementState(){const{value:e,selectionStart:s,selectionEnd:t}=this.element;return{value:e,selection:[s||0,t||0]}}get maxLength(){const{maxLength:e}=this.element;return e===-1?1/0:e}destroy(){this.eventListener.destroy(),this.teardowns.forEach(e=>e==null?void 0:e())}updateElementState({value:e,selection:s},t={inputType:"insertText",data:null}){const i=this.elementState.value;this.updateValue(e),this.updateSelectionRange(s),i!==e&&this.dispatchInputEvent(t)}updateSelectionRange([e,s]){var t;const{element:i}=this;i.matches(":focus")&&(i.selectionStart!==e||i.selectionEnd!==s)&&((t=i.setSelectionRange)===null||t===void 0||t.call(i,e,s))}updateValue(e){this.element.value=e}ensureValueFitsMask(){this.updateElementState(fe(this.elementState,this.options))}dispatchInputEvent(e={inputType:"insertText",data:null}){globalThis.InputEvent&&this.element.dispatchEvent(new InputEvent("input",Object.assign(Object.assign({},e),{bubbles:!0,cancelable:!1})))}handleDelete({event:e,selection:s,isForward:t,force:i=!1}){const r={value:this.elementState.value,selection:s},[l,o]=r.selection,{elementState:a}=this.preprocessor({elementState:r,data:""},t?"deleteForward":"deleteBackward"),c=new M(a,this.options),[u,d]=a.selection;c.deleteCharacters([u,d]);const h=this.postprocessor(c,r);if(!(r.value.slice(0,l)+r.value.slice(o)===h.value&&!i&&!this.element.isContentEditable)){if(e.preventDefault(),ee(r,a,c,h))return this.updateSelectionRange(t?[d,d]:[u,u]);this.updateElementState(h,{inputType:e.inputType,data:null}),this.updateHistory(h)}}handleInsert(e,s){const t=this.elementState,{elementState:i,data:r=s}=this.preprocessor({data:s,elementState:t},"insert"),l=new M(i,this.options);try{l.addCharacters(i.selection,r)}catch{return e.preventDefault()}const[o,a]=i.selection,c=t.value.slice(0,o)+s+t.value.slice(a),u=this.postprocessor(l,t);if(u.value.length>this.maxLength)return e.preventDefault();(c!==u.value||this.element.isContentEditable)&&(e.preventDefault(),this.updateElementState(u,{data:s,inputType:e.inputType}),this.updateHistory(u))}handleEnter(e){(this.isTextArea||this.element.isContentEditable)&&this.handleInsert(e,` +`)}}function me(n,e,s){const t=Math.min(Number(s),Math.max(Number(e),Number(n)));return n instanceof Date?new Date(t):t}function pe(n){return n.replaceAll(/\W/g,"").length}const N=n=>{var e,s,t;return{day:((e=n.match(/d/g))===null||e===void 0?void 0:e.length)||0,month:((s=n.match(/m/g))===null||s===void 0?void 0:s.length)||0,year:((t=n.match(/y/g))===null||t===void 0?void 0:t.length)||0}};function ve(n){return{day:String(n.getDate()).padStart(2,"0"),month:String(n.getMonth()+1).padStart(2,"0"),year:String(n.getFullYear()).padStart(4,"0"),hours:String(n.getHours()).padStart(2,"0"),minutes:String(n.getMinutes()).padStart(2,"0"),seconds:String(n.getSeconds()).padStart(2,"0"),milliseconds:String(n.getMilliseconds()).padStart(3,"0")}}function ge(n,e){return n.length!s.match(/^0+$/))}function P(n,e,s){const t=pe(e);return n.replace(s,"").match(new RegExp(`(\\D*\\d[^\\d\\s]*){1,${t}}`,"g"))||[]}function O(n,e){const s=e.replaceAll(/[^dmy]/g,""),t=n.replaceAll(/\D+/g,""),i={day:t.slice(s.indexOf("d"),s.lastIndexOf("d")+1),month:t.slice(s.indexOf("m"),s.lastIndexOf("m")+1),year:t.slice(s.indexOf("y"),s.lastIndexOf("y")+1)};return Object.fromEntries(Object.entries(i).filter(([r,l])=>!!l).sort(([r],[l])=>e.toLowerCase().indexOf(r[0])>e.toLowerCase().indexOf(l[0])?1:-1))}function ye(n,e){var s,t,i,r,l,o,a;const c=((s=n.year)===null||s===void 0?void 0:s.length)===2?`20${n.year}`:n.year,u=new Date(Number(c??"0"),Number((t=n.month)!==null&&t!==void 0?t:"1")-1,Number((i=n.day)!==null&&i!==void 0?i:"1"),Number((r=void 0)!==null&&r!==void 0?r:"0"),Number((l=void 0)!==null&&l!==void 0?l:"0"),Number((o=void 0)!==null&&o!==void 0?o:"0"),Number((a=void 0)!==null&&a!==void 0?a:"0"));return u.setFullYear(Number(c??"0")),u}const B=", ";function k({day:n,month:e,year:s,hours:t,minutes:i,seconds:r,milliseconds:l},{dateMode:o,dateTimeSeparator:a=B,timeMode:c}){var u;const d=((u=o.match(/y/g))===null||u===void 0?void 0:u.length)===2?s==null?void 0:s.slice(-2):s;return(o+(c?a+c:"")).replaceAll(/d+/g,n??"").replaceAll(/m+/g,e??"").replaceAll(/y+/g,d??"").replaceAll(/H+/g,t??"").replaceAll("MSS",l??"").replaceAll(/M+/g,i??"").replaceAll(/S+/g,r??"").replaceAll(/^\D+/g,"").replaceAll(/\D+$/g,"")}const W={day:31,month:12,year:9999},Se=new Date("0001-01-01"),Ee=new Date("9999-12-31"),we=[":","."];function Ae({dateString:n,dateModeTemplate:e,dateSegmentsSeparator:s,offset:t,selection:[i,r]}){const l=O(n,e),o=Object.entries(l),a={};for(const[d,h]of o){const p=k(a,{dateMode:e}),A=W[d],S=p.length&&s.length,y=t+p.length+S+N(e)[d],E=y>=i&&y===r;if(E&&Number(h)>Number(A))return{validatedDateString:"",updatedSelection:[i,r]};if(E&&Number(h)<1)return{validatedDateString:"",updatedSelection:[i,r]};a[d]=h}const c=k(a,{dateMode:e}),u=c.length-n.length;return{validatedDateString:c,updatedSelection:[i+u,r+u]}}const U=/[\\^$.*+?()[\]{}|]/g,be=new RegExp(U.source);function G(n){return n&&be.test(n)?n.replaceAll(U,"\\$&"):n}function R(n,e,s=0){return Number(n.padEnd(e.length,"0"))<=Number(e)?{validatedSegmentValue:n,prefixedZeroesCount:s}:n.endsWith("0")?R(`0${n.slice(0,e.length-1)}`,e,s+1):R(`${n.slice(0,e.length-1)}0`,e,s)}function Z(n){return n.replaceAll(/[0-9]/g,e=>String.fromCharCode(e.charCodeAt(0)-65248))}function De({dateModeTemplate:n,dateSegmentSeparator:e,splitFn:s,uniteFn:t}){return({value:i,selection:r})=>{var l;const[o,a]=r,{dateStrings:c,restPart:u=""}=s(i),d=[];let h=0;c.forEach(A=>{const S=O(A,n),E=Object.entries(S).reduce((_,[K,Fe])=>{const{validatedSegmentValue:_e,prefixedZeroesCount:je}=R(Fe,`${W[K]}`);return h+=je,Object.assign(Object.assign({},_),{[K]:_e})},{});d.push(k(E,{dateMode:n}))});const p=t(d,i)+(!((l=c[c.length-1])===null||l===void 0)&&l.endsWith(e)?e:"")+u;return h&&p.slice(a+h,a+h+e.length)===e&&(h+=e.length),{selection:[o+h,a+h],value:p}}}function ke(){return({elementState:n,data:e})=>{const{value:s,selection:t}=n;return{elementState:{selection:t,value:Z(s)},data:Z(e)}}}function Te(n,e){const s=N(e);return Object.fromEntries(Object.entries(n).map(([t,i])=>{const r=s[t];return[t,i.length===r&&i.match(/^0+$/)?"1".padStart(r,"0"):i]}))}function Ce({dateModeTemplate:n,min:e=Se,max:s=Ee,rangeSeparator:t="",dateSegmentSeparator:i="."}){return({value:r,selection:l})=>{const o=t&&r.endsWith(t),a=P(r,n,t);let c="";for(const u of a){c+=c?t:"";const d=O(u,n);if(!ge(u,n)){const A=Te(d,n),S=k(A,{dateMode:n}),y=u.endsWith(i)?i:"";c+=S+y;continue}const h=ye(d),p=me(h,e,s);c+=k(ve(p),{dateMode:n})}return{selection:l,value:c+(o?t:"")}}}function xe({dateModeTemplate:n,dateSegmentsSeparator:e,rangeSeparator:s="",dateTimeSeparator:t=B}){return({elementState:i,data:r})=>{const l=s?new RegExp(`${s}|-`):t,o=r.split(l),a=r.includes(t)?[o[0]]:o;if(a.every(c=>c.trim().split(/\D/).filter(Boolean).length===n.split(e).length)){const c=a.map(u=>Le(u,n,e)).join(s);return{elementState:i,data:`${c}${r.includes(t)&&t+o[1]||""}`}}return{elementState:i,data:r}}}function Le(n,e,s){const t=n.split(/\D/).filter(Boolean),i=e.split(s);return t.map((l,o)=>o===i.length-1?l:l.padStart(i[o].length,"0")).join(s)}function Me({dateModeTemplate:n,dateSegmentsSeparator:e,rangeSeparator:s=""}){return({elementState:t,data:i})=>{const{value:r,selection:l}=t;if(i===e)return{elementState:t,data:l[0]===r.length?i:""};const o=i.replaceAll(new RegExp(`[^\\d${G(e)}${s}]`,"g"),"");if(!o)return{elementState:t,data:""};const[a,c]=l;let u=c+i.length;const d=r.slice(0,a)+o+r.slice(u),h=P(d,n,s);let p="";const A=!!s&&d.includes(s);for(const y of h){const{validatedDateString:E,updatedSelection:_}=Ae({dateString:y,dateModeTemplate:n,dateSegmentsSeparator:e,offset:p.length,selection:[a,u]});if(y&&!E)return{elementState:t,data:""};u=_[1],p+=A&&!p?E+s:E}const S=p.slice(a,u);return{elementState:{selection:l,value:p.slice(0,a)+S.split(e).map(y=>"0".repeat(y.length)).join(e)+p.slice(u)},data:S}}}function Ie(){return({elementState:n},e)=>{const{value:s,selection:t}=n;if(!s||Oe(s,t))return{elementState:n};const[i,r]=t,l=s.slice(i,r).replaceAll(/\d/g,"0"),o=s.slice(0,i)+l+s.slice(r);return e==="validation"||e==="insert"&&i===r?{elementState:{selection:t,value:o}}:{elementState:{selection:e==="deleteBackward"||e==="insert"?[i,i]:[r,r],value:o}}}}function Oe(n,[e,s]){return s===n.length}function Re({mode:n,separator:e=".",max:s,min:t}){const i=n.split("/").join(e);return Object.assign(Object.assign({},v),{mask:Array.from(i).map(r=>e.includes(r)?r:/\d/),overwriteMode:"replace",preprocessors:[ke(),Ie(),xe({dateModeTemplate:i,dateSegmentsSeparator:e}),Me({dateModeTemplate:i,dateSegmentsSeparator:e})],postprocessors:[De({dateModeTemplate:i,dateSegmentSeparator:e,splitFn:r=>({dateStrings:[r]}),uniteFn:([r])=>r}),Ce({min:t,max:s,dateModeTemplate:i,dateSegmentSeparator:e})]})}new RegExp(`[${we.map(G).join("")}]$`);const q=/^(?:\d{4}[ -]?){0,3}\d{0,4}$/,f={visa:{final:/^4[0-9]{12}(?:[0-9]{3})?$/,start:/^4/,mask:[...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(3).fill(/\d/)]},mastercard:{final:/^5[1-5][0-9]{14}$/,start:/^5[1-5]/,mask:[...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)]},amex:{final:/^3[47][0-9]{13}$/,start:/^3[47]/,mask:[...new Array(4).fill(/\d/)," ",...new Array(6).fill(/\d/)," ",...new Array(5).fill(/\d/)]},discover:{final:/^6(?:011|5[0-9]{2})[0-9]{12}$/,start:/^(6011|65|64[4-9])/,mask:[...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)]},diners:{final:/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,start:/^(30[0-5]|36|38|39)/,mask:[...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)]},jcb:{final:/^(?:2131|1800|35\d{3})\d{11}$/,start:/^(2131|1800|35[0-9]{3})/,mask:[...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(4).fill(/\d/)," ",...new Array(3).fill(/\d/)]}};class F{constructor(e){X(this,L);this.options=e}mount(){return this.number=this.options.fields.card.number instanceof HTMLInputElement?this.options.fields.card.number:document.querySelector(this.options.fields.card.number),this.date=this.options.fields.card.date instanceof HTMLInputElement?this.options.fields.card.date:document.querySelector(this.options.fields.card.date),this.cvv=this.options.fields.card.cvv instanceof HTMLInputElement?this.options.fields.card.cvv:document.querySelector(this.options.fields.card.cvv),J(this,L,Q).call(this),this}check(){const e=f.visa.final.test(this.value("number"))||f.mastercard.final.test(this.value("number"))||f.amex.final.test(this.value("number"))||f.discover.final.test(this.value("number"))||f.diners.final.test(this.value("number"))||f.jcb.final.test(this.value("number")),s=new RegExp("^(0[1-9]|1[0-2])/(?:\\d{2})$").test(this.date.value),t=new RegExp("^\\d{3}$").test(this.cvv.value);return{valid:e&&s&&t,number:{valid:e,value:this.number.value},date:{valid:s,value:this.date.value},cvv:{valid:t,value:this.cvv.value}}}type(){return f.visa.start.test(this.number.value)?"visa":f.mastercard.start.test(this.number.value)?"mastercard":f.amex.start.test(this.number.value)?"amex":f.discover.start.test(this.number.value)?"discover":f.diners.start.test(this.number.value)?"diners":f.jcb.start.test(this.number.value)?"jcb":"unknown"}value(e){if(e==="number")return this.number.value.replace(/\s+/g,"");if(e==="date")return this.date.value;if(e==="year"){const[,s]=this.date.value.split("/");return s}if(e==="month"){const[s]=this.date.value.split("/");return s}return e==="cvv"?this.cvv.value:null}}L=new WeakSet,Q=function(){new I(this.number,{mask:e=>f.visa.start.test(e.value)?f.visa.mask:f.mastercard.start.test(e.value)?f.mastercard.mask:f.amex.start.test(e.value)?f.amex.mask:f.discover.start.test(e.value)?f.discover.mask:f.diners.start.test(e.value)?f.diners.mask:f.jcb.start.test(e.value)?f.jcb.mask:new RegExp(q)}),new I(this.date,Re({mode:"mm/yy",separator:"/"})),new I(this.cvv,{mask:[/\d/,/\d/,/\d/]})},window.SimpleCard=F,window.createSimpleCard=z;function z(n){return new F(n)}return m.SimpleCard=F,m.createSimpleCard=z,m.masks=f,m.numbers=q,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"}),m}({}); diff --git a/public/vendor/simple-card@alpha/simple-card.d.ts b/public/vendor/simple-card@alpha/simple-card.d.ts deleted file mode 100644 index bb6dc52e8110..000000000000 --- a/public/vendor/simple-card@alpha/simple-card.d.ts +++ /dev/null @@ -1,79 +0,0 @@ -export declare const masks: { - visa: { - final: RegExp; - start: RegExp; - length: RegExp; - }; - mastercard: { - final: RegExp; - start: RegExp; - length: RegExp; - }; - amex: { - final: RegExp; - start: RegExp; - length: RegExp; - }; - discover: { - final: RegExp; - start: RegExp; - length: RegExp; - }; - diners: { - final: RegExp; - start: RegExp; - length: RegExp; - }; - jcb: { - final: RegExp; - start: RegExp; - length: RegExp; - }; -}; - -export declare const numbers: RegExp; - -export declare type Options = { - fields: { - card: { - number: string | HTMLInputElement; - date: string | HTMLInputElement; - cvv: string | HTMLInputElement; - name?: string | HTMLInputElement; - }; - }; -}; - -export declare class SimpleCard { - #private; - options: Options; - number: HTMLInputElement; - date: HTMLInputElement; - cvv: HTMLInputElement; - constructor(options: Options); - mount(): this; - check(): { - valid: boolean; - number: { - valid: boolean; - value: string; - }; - date: { - valid: boolean; - value: string; - }; - cvv: { - valid: boolean; - value: string; - }; - }; - type(): "visa" | "mastercard" | "amex" | "discover" | "diners" | "jcb" | "unknown"; -} - -export declare type TypeChangeOptions = { - type: string; - value: string; - valid: boolean; -}; - -export { } diff --git a/public/vendor/simple-card@alpha/simple-card.js b/public/vendor/simple-card@alpha/simple-card.js deleted file mode 100644 index de4c51207408..000000000000 --- a/public/vendor/simple-card@alpha/simple-card.js +++ /dev/null @@ -1,734 +0,0 @@ -var F = (n) => { - throw TypeError(n); -}; -var J = (n, e, s) => e.has(n) || F("Cannot " + s); -var _ = (n, e, s) => e.has(n) ? F("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(n) : e.set(n, s); -var $ = (n, e, s) => (J(n, e, "access private method"), s); -const I = { - mask: /^.*$/, - preprocessors: [], - postprocessors: [], - plugins: [], - overwriteMode: "shift" -}; -class Q { - constructor() { - this.now = null, this.past = [], this.future = []; - } - undo() { - const e = this.past.pop(); - e && this.now && (this.future.push(this.now), this.updateElement(e, "historyUndo")); - } - redo() { - const e = this.future.pop(); - e && this.now && (this.past.push(this.now), this.updateElement(e, "historyRedo")); - } - updateHistory(e) { - if (!this.now) { - this.now = e; - return; - } - const s = this.now.value !== e.value, t = this.now.selection.some((i, r) => i !== e.selection[r]); - !s && !t || (s && (this.past.push(this.now), this.future = []), this.now = e); - } - updateElement(e, s) { - this.now = e, this.updateElementState(e, { inputType: s, data: null }); - } -} -function ee(n, ...e) { - return e.every(({ value: s }) => s === n.value); -} -function te(n, ...e) { - return e.every(({ value: s, selection: t }) => s === n.value && t[0] === n.selection[0] && t[1] === n.selection[1]); -} -function ne({ value: n, selection: e }, s, t) { - const [i, r] = e, l = typeof t == "function" ? t({ value: n, selection: e }) : t; - return { - value: n, - selection: l === "replace" ? [i, i + s.length] : [i, r] - }; -} -function T(n) { - return typeof n == "string"; -} -function j(n, e, s, t) { - let i = ""; - for (let r = e.length; r < n.length; r++) { - const l = n[r], a = (t == null ? void 0 : t.value[r]) === l; - if (!T(l) || l === s && !a) - return i; - i += l; - } - return i; -} -function P(n, e) { - return Array.isArray(e) ? n.length === e.length && Array.from(n).every((s, t) => { - const i = e[t]; - return T(i) ? s === i : s.match(i); - }) : e.test(n); -} -function se(n, e, s) { - let t = null, i = null; - const r = Array.from(n.value).reduce((a, o, c) => { - const u = j(e, a, o, s), d = a + u, h = e[d.length]; - return T(h) ? d + h : o.match(h) ? (t === null && c >= n.selection[0] && (t = d.length), i === null && c >= n.selection[1] && (i = d.length), d + o) : d; - }, ""), l = j(e, r, "", s); - return { - value: P(r + l, e) ? r + l : r, - selection: [t ?? r.length, i ?? r.length] - }; -} -function ie({ value: n, selection: e }, s) { - const [t, i] = e; - let r = t, l = i; - return { value: Array.from(n).reduce((o, c, u) => { - const d = o + c; - return t === u && (r = o.length), i === u && (l = o.length), d.match(s) ? d : o; - }, ""), selection: [r, l] }; -} -function D(n, e, s = null) { - if (P(n.value, e)) - return n; - const { value: t, selection: i } = Array.isArray(e) ? se(n, e, s) : ie(n, e); - return { - selection: i, - value: Array.isArray(e) ? t.slice(0, e.length) : t - }; -} -function H(n, e) { - if (!Array.isArray(e)) - return n; - const [s, t] = n.selection, i = [], r = Array.from(n.value).reduce((l, a, o) => { - const c = e[o]; - return o === s && i.push(l.length), o === t && i.push(l.length), T(c) && c === a ? l : l + a; - }, ""); - return i.length < 2 && i.push(...new Array(2 - i.length).fill(r.length)), { - value: r, - selection: [i[0], i[1]] - }; -} -class L { - constructor(e, s) { - this.initialElementState = e, this.maskOptions = s, this.value = "", this.selection = [0, 0]; - const { value: t, selection: i } = D(this.initialElementState, this.getMaskExpression(this.initialElementState)); - this.value = t, this.selection = i; - } - addCharacters([e, s], t) { - const { value: i } = this, r = this.getMaskExpression({ - value: i.slice(0, e) + t + i.slice(s), - selection: [e + t.length, e + t.length] - }), l = { value: i, selection: [e, s] }, a = H(l, r), [o, c] = ne(a, t, this.maskOptions.overwriteMode).selection, u = a.value.slice(0, o) + t, d = u.length, h = D({ - value: u + a.value.slice(c), - selection: [d, d] - }, r, l); - if (// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - i.slice(0, o) === D({ - value: u, - selection: [d, d] - }, r, l).value || te(this, h)) - throw new Error("Invalid mask value"); - this.value = h.value, this.selection = h.selection; - } - deleteCharacters([e, s]) { - if (e === s || !s) - return; - const { value: t } = this, i = this.getMaskExpression({ - value: t.slice(0, e) + t.slice(s), - selection: [e, e] - }), r = { value: t, selection: [e, s] }, l = H(r, i), [a, o] = l.selection, c = l.value.slice(0, a) + l.value.slice(o), u = D({ value: c, selection: [a, a] }, i, r); - this.value = u.value, this.selection = u.selection; - } - getMaskExpression(e) { - const { mask: s } = this.maskOptions; - return typeof s == "function" ? s(e) : s; - } -} -class re { - constructor(e) { - this.element = e, this.listeners = []; - } - listen(e, s, t) { - const i = s; - this.element.addEventListener(e, i, t), this.listeners.push(() => this.element.removeEventListener(e, i)); - } - destroy() { - this.listeners.forEach((e) => e()); - } -} -const g = { - CTRL: 1, - ALT: 2, - SHIFT: 4, - META: 8 -}, y = { - Y: 89, - Z: 90 -}; -function b(n, e, s) { - return n.ctrlKey === !!(e & g.CTRL) && n.altKey === !!(e & g.ALT) && n.shiftKey === !!(e & g.SHIFT) && n.metaKey === !!(e & g.META) && /** - * We intentionally use legacy {@link KeyboardEvent#keyCode `keyCode`} property. It is more - * "keyboard-layout"-independent than {@link KeyboardEvent#key `key`} or {@link KeyboardEvent#code `code`} properties. - * @see {@link https://github.com/taiga-family/maskito/issues/315 `KeyboardEvent#code` issue} - */ - n.keyCode === s; -} -function le(n) { - return b(n, g.CTRL, y.Y) || // Windows - b(n, g.CTRL | g.SHIFT, y.Z) || // Windows & Android - b(n, g.META | g.SHIFT, y.Z); -} -function oe(n) { - return b(n, g.CTRL, y.Z) || // Windows & Android - b(n, g.META, y.Z); -} -function ae({ value: n, selection: e }, s) { - const [t, i] = e; - if (t !== i) - return [t, i]; - const r = s ? n.slice(t).indexOf(` -`) + 1 || n.length : n.slice(0, i).lastIndexOf(` -`) + 1; - return [s ? t : r, s ? r : i]; -} -function ce({ value: n, selection: e }, s) { - const [t, i] = e; - return t !== i ? [t, i] : (s ? [t, i + 1] : [t - 1, i]).map((l) => Math.min(Math.max(l, 0), n.length)); -} -const ue = /\s+$/g, de = /^\s+/g, V = /\s/; -function he({ value: n, selection: e }, s) { - const [t, i] = e; - if (t !== i) - return [t, i]; - if (s) { - const o = n.slice(t), [c] = o.match(de) || [ - "" - ], u = o.trimStart().search(V); - return [ - t, - u !== -1 ? t + c.length + u : n.length - ]; - } - const r = n.slice(0, i), [l] = r.match(ue) || [""], a = r.trimEnd().split("").reverse().findIndex((o) => o.match(V)); - return [ - a !== -1 ? i - l.length - a : 0, - i - ]; -} -function A(n = []) { - return (e, ...s) => n.reduce((t, i) => Object.assign(Object.assign({}, t), i(t, ...s)), e); -} -function pe(n, e) { - const s = Object.assign(Object.assign({}, I), e), t = A(s.preprocessors), i = A(s.postprocessors), r = typeof n == "string" ? { value: n, selection: [0, 0] } : n, { elementState: l } = t({ elementState: r, data: "" }, "validation"), a = new L(l, s), { value: o, selection: c } = i(a, r); - return typeof n == "string" ? o : { value: o, selection: c }; -} -class C extends Q { - constructor(e, s) { - super(), this.element = e, this.maskitoOptions = s, this.isTextArea = this.element.nodeName === "TEXTAREA", this.eventListener = new re(this.element), this.options = Object.assign(Object.assign({}, I), this.maskitoOptions), this.preprocessor = A(this.options.preprocessors), this.postprocessor = A(this.options.postprocessors), this.teardowns = this.options.plugins.map((t) => t(this.element, this.options)), this.updateHistory(this.elementState), this.eventListener.listen("keydown", (t) => { - if (le(t)) - return t.preventDefault(), this.redo(); - if (oe(t)) - return t.preventDefault(), this.undo(); - }), this.eventListener.listen("beforeinput", (t) => { - var i; - const r = t.inputType.includes("Forward"); - switch (this.updateHistory(this.elementState), t.inputType) { - case "historyUndo": - return t.preventDefault(), this.undo(); - case "historyRedo": - return t.preventDefault(), this.redo(); - case "deleteByCut": - case "deleteContentBackward": - case "deleteContentForward": - return this.handleDelete({ - event: t, - isForward: r, - selection: ce(this.elementState, r) - }); - case "deleteWordForward": - case "deleteWordBackward": - return this.handleDelete({ - event: t, - isForward: r, - selection: he(this.elementState, r), - force: !0 - }); - case "deleteSoftLineBackward": - case "deleteSoftLineForward": - case "deleteHardLineBackward": - case "deleteHardLineForward": - return this.handleDelete({ - event: t, - isForward: r, - selection: ae(this.elementState, r), - force: !0 - }); - case "insertCompositionText": - return; - case "insertReplacementText": - return; - case "insertLineBreak": - case "insertParagraph": - return this.handleEnter(t); - case "insertFromPaste": - case "insertText": - case "insertFromDrop": - default: - return this.handleInsert(t, t.data || // `event.data` for `contentEditable` is always `null` for paste/drop events - ((i = t.dataTransfer) === null || i === void 0 ? void 0 : i.getData("text/plain")) || ""); - } - }), this.eventListener.listen("input", ({ inputType: t }) => { - t !== "insertCompositionText" && (this.ensureValueFitsMask(), this.updateHistory(this.elementState)); - }), this.eventListener.listen("compositionend", () => { - this.ensureValueFitsMask(), this.updateHistory(this.elementState); - }); - } - get elementState() { - const { value: e, selectionStart: s, selectionEnd: t } = this.element; - return { - value: e, - selection: [s || 0, t || 0] - }; - } - get maxLength() { - const { maxLength: e } = this.element; - return e === -1 ? 1 / 0 : e; - } - destroy() { - this.eventListener.destroy(), this.teardowns.forEach((e) => e == null ? void 0 : e()); - } - updateElementState({ value: e, selection: s }, t = { - inputType: "insertText", - data: null - }) { - const i = this.elementState.value; - this.updateValue(e), this.updateSelectionRange(s), i !== e && this.dispatchInputEvent(t); - } - updateSelectionRange([e, s]) { - var t; - const { element: i } = this; - i.matches(":focus") && (i.selectionStart !== e || i.selectionEnd !== s) && ((t = i.setSelectionRange) === null || t === void 0 || t.call(i, e, s)); - } - updateValue(e) { - this.element.value = e; - } - ensureValueFitsMask() { - this.updateElementState(pe(this.elementState, this.options)); - } - dispatchInputEvent(e = { - inputType: "insertText", - data: null - }) { - globalThis.InputEvent && this.element.dispatchEvent(new InputEvent("input", Object.assign(Object.assign({}, e), { bubbles: !0, cancelable: !1 }))); - } - handleDelete({ event: e, selection: s, isForward: t, force: i = !1 }) { - const r = { - value: this.elementState.value, - selection: s - }, [l, a] = r.selection, { elementState: o } = this.preprocessor({ - elementState: r, - data: "" - }, t ? "deleteForward" : "deleteBackward"), c = new L(o, this.options), [u, d] = o.selection; - c.deleteCharacters([u, d]); - const h = this.postprocessor(c, r); - if (!(r.value.slice(0, l) + r.value.slice(a) === h.value && !i && !this.element.isContentEditable)) { - if (e.preventDefault(), ee(r, o, c, h)) - return this.updateSelectionRange(t ? [d, d] : [u, u]); - this.updateElementState(h, { - inputType: e.inputType, - data: null - }), this.updateHistory(h); - } - } - handleInsert(e, s) { - const t = this.elementState, { elementState: i, data: r = s } = this.preprocessor({ - data: s, - elementState: t - }, "insert"), l = new L(i, this.options); - try { - l.addCharacters(i.selection, r); - } catch { - return e.preventDefault(); - } - const [a, o] = i.selection, c = t.value.slice(0, a) + s + t.value.slice(o), u = this.postprocessor(l, t); - if (u.value.length > this.maxLength) - return e.preventDefault(); - (c !== u.value || this.element.isContentEditable) && (e.preventDefault(), this.updateElementState(u, { - data: s, - inputType: e.inputType - }), this.updateHistory(u)); - } - handleEnter(e) { - (this.isTextArea || this.element.isContentEditable) && this.handleInsert(e, ` -`); - } -} -function fe(n, e, s) { - const t = Math.min(Number(s), Math.max(Number(e), Number(n))); - return n instanceof Date ? new Date(t) : t; -} -function ge(n) { - return n.replaceAll(/\W/g, "").length; -} -const B = (n) => { - var e, s, t; - return { - day: ((e = n.match(/d/g)) === null || e === void 0 ? void 0 : e.length) || 0, - month: ((s = n.match(/m/g)) === null || s === void 0 ? void 0 : s.length) || 0, - year: ((t = n.match(/y/g)) === null || t === void 0 ? void 0 : t.length) || 0 - }; -}; -function me(n) { - return { - day: String(n.getDate()).padStart(2, "0"), - month: String(n.getMonth() + 1).padStart(2, "0"), - year: String(n.getFullYear()).padStart(4, "0"), - hours: String(n.getHours()).padStart(2, "0"), - minutes: String(n.getMinutes()).padStart(2, "0"), - seconds: String(n.getSeconds()).padStart(2, "0"), - milliseconds: String(n.getMilliseconds()).padStart(3, "0") - }; -} -function ve(n, e) { - return n.length < e.length ? !1 : n.split(/\D/).every((s) => !s.match(/^0+$/)); -} -function W(n, e, s) { - const t = ge(e); - return n.replace(s, "").match(new RegExp(`(\\D*\\d[^\\d\\s]*){1,${t}}`, "g")) || []; -} -function M(n, e) { - const s = e.replaceAll(/[^dmy]/g, ""), t = n.replaceAll(/\D+/g, ""), i = { - day: t.slice(s.indexOf("d"), s.lastIndexOf("d") + 1), - month: t.slice(s.indexOf("m"), s.lastIndexOf("m") + 1), - year: t.slice(s.indexOf("y"), s.lastIndexOf("y") + 1) - }; - return Object.fromEntries(Object.entries(i).filter(([r, l]) => !!l).sort(([r], [l]) => e.toLowerCase().indexOf(r[0]) > e.toLowerCase().indexOf(l[0]) ? 1 : -1)); -} -function Ee(n, e) { - var s, t, i, r, l, a, o; - const c = ((s = n.year) === null || s === void 0 ? void 0 : s.length) === 2 ? `20${n.year}` : n.year, u = new Date(Number(c ?? "0"), Number((t = n.month) !== null && t !== void 0 ? t : "1") - 1, Number((i = n.day) !== null && i !== void 0 ? i : "1"), Number((r = void 0) !== null && r !== void 0 ? r : "0"), Number((l = void 0) !== null && l !== void 0 ? l : "0"), Number((a = void 0) !== null && a !== void 0 ? a : "0"), Number((o = void 0) !== null && o !== void 0 ? o : "0")); - return u.setFullYear(Number(c ?? "0")), u; -} -const U = ", "; -function w({ day: n, month: e, year: s, hours: t, minutes: i, seconds: r, milliseconds: l }, { dateMode: a, dateTimeSeparator: o = U, timeMode: c }) { - var u; - const d = ((u = a.match(/y/g)) === null || u === void 0 ? void 0 : u.length) === 2 ? s == null ? void 0 : s.slice(-2) : s; - return (a + (c ? o + c : "")).replaceAll(/d+/g, n ?? "").replaceAll(/m+/g, e ?? "").replaceAll(/y+/g, d ?? "").replaceAll(/H+/g, t ?? "").replaceAll("MSS", l ?? "").replaceAll(/M+/g, i ?? "").replaceAll(/S+/g, r ?? "").replaceAll(/^\D+/g, "").replaceAll(/\D+$/g, ""); -} -const G = { - day: 31, - month: 12, - year: 9999 -}, Se = /* @__PURE__ */ new Date("0001-01-01"), ye = /* @__PURE__ */ new Date("9999-12-31"), be = [":", "."]; -function we({ dateString: n, dateModeTemplate: e, dateSegmentsSeparator: s, offset: t, selection: [i, r] }) { - const l = M(n, e), a = Object.entries(l), o = {}; - for (const [d, h] of a) { - const f = w(o, { - dateMode: e - }), S = G[d], v = f.length && s.length, m = t + f.length + v + B(e)[d], E = m >= i && m === r; - if (E && Number(h) > Number(S)) - return { validatedDateString: "", updatedSelection: [i, r] }; - if (E && Number(h) < 1) - return { validatedDateString: "", updatedSelection: [i, r] }; - o[d] = h; - } - const c = w(o, { - dateMode: e - }), u = c.length - n.length; - return { - validatedDateString: c, - updatedSelection: [ - i + u, - r + u - ] - }; -} -const Z = /[\\^$.*+?()[\]{}|]/g, De = new RegExp(Z.source); -function q(n) { - return n && De.test(n) ? n.replaceAll(Z, "\\$&") : n; -} -function R(n, e, s = 0) { - return Number(n.padEnd(e.length, "0")) <= Number(e) ? { validatedSegmentValue: n, prefixedZeroesCount: s } : n.endsWith("0") ? R(`0${n.slice(0, e.length - 1)}`, e, s + 1) : R(`${n.slice(0, e.length - 1)}0`, e, s); -} -function N(n) { - return n.replaceAll(/[0-9]/g, (e) => String.fromCharCode(e.charCodeAt(0) - 65248)); -} -function Ae({ dateModeTemplate: n, dateSegmentSeparator: e, splitFn: s, uniteFn: t }) { - return ({ value: i, selection: r }) => { - var l; - const [a, o] = r, { dateStrings: c, restPart: u = "" } = s(i), d = []; - let h = 0; - c.forEach((S) => { - const v = M(S, n), E = Object.entries(v).reduce((k, [O, K]) => { - const { validatedSegmentValue: Y, prefixedZeroesCount: X } = R(K, `${G[O]}`); - return h += X, Object.assign(Object.assign({}, k), { [O]: Y }); - }, {}); - d.push(w(E, { dateMode: n })); - }); - const f = t(d, i) + (!((l = c[c.length - 1]) === null || l === void 0) && l.endsWith(e) ? e : "") + u; - return h && f.slice(o + h, o + h + e.length) === e && (h += e.length), { - selection: [a + h, o + h], - value: f - }; - }; -} -function xe() { - return ({ elementState: n, data: e }) => { - const { value: s, selection: t } = n; - return { - elementState: { - selection: t, - value: N(s) - }, - data: N(e) - }; - }; -} -function Te(n, e) { - const s = B(e); - return Object.fromEntries(Object.entries(n).map(([t, i]) => { - const r = s[t]; - return [ - t, - i.length === r && i.match(/^0+$/) ? "1".padStart(r, "0") : i - ]; - })); -} -function ke({ dateModeTemplate: n, min: e = Se, max: s = ye, rangeSeparator: t = "", dateSegmentSeparator: i = "." }) { - return ({ value: r, selection: l }) => { - const a = t && r.endsWith(t), o = W(r, n, t); - let c = ""; - for (const u of o) { - c += c ? t : ""; - const d = M(u, n); - if (!ve(u, n)) { - const S = Te(d, n), v = w(S, { dateMode: n }), m = u.endsWith(i) ? i : ""; - c += v + m; - continue; - } - const h = Ee(d), f = fe(h, e, s); - c += w(me(f), { - dateMode: n - }); - } - return { - selection: l, - value: c + (a ? t : "") - }; - }; -} -function Ce({ dateModeTemplate: n, dateSegmentsSeparator: e, rangeSeparator: s = "", dateTimeSeparator: t = U }) { - return ({ elementState: i, data: r }) => { - const l = s ? new RegExp(`${s}|-`) : t, a = r.split(l), o = r.includes(t) ? [a[0]] : a; - if (o.every((c) => c.trim().split(/\D/).filter(Boolean).length === n.split(e).length)) { - const c = o.map((u) => Le(u, n, e)).join(s); - return { - elementState: i, - data: `${c}${r.includes(t) && t + a[1] || ""}` - }; - } - return { elementState: i, data: r }; - }; -} -function Le(n, e, s) { - const t = n.split(/\D/).filter(Boolean), i = e.split(s); - return t.map((l, a) => a === i.length - 1 ? l : l.padStart(i[a].length, "0")).join(s); -} -function Re({ dateModeTemplate: n, dateSegmentsSeparator: e, rangeSeparator: s = "" }) { - return ({ elementState: t, data: i }) => { - const { value: r, selection: l } = t; - if (i === e) - return { - elementState: t, - data: l[0] === r.length ? i : "" - }; - const a = i.replaceAll(new RegExp(`[^\\d${q(e)}${s}]`, "g"), ""); - if (!a) - return { elementState: t, data: "" }; - const [o, c] = l; - let u = c + i.length; - const d = r.slice(0, o) + a + r.slice(u), h = W(d, n, s); - let f = ""; - const S = !!s && d.includes(s); - for (const m of h) { - const { validatedDateString: E, updatedSelection: k } = we({ - dateString: m, - dateModeTemplate: n, - dateSegmentsSeparator: e, - offset: f.length, - selection: [o, u] - }); - if (m && !E) - return { elementState: t, data: "" }; - u = k[1], f += S && !f ? E + s : E; - } - const v = f.slice(o, u); - return { - elementState: { - selection: l, - value: f.slice(0, o) + v.split(e).map((m) => "0".repeat(m.length)).join(e) + f.slice(u) - }, - data: v - }; - }; -} -function Ie() { - return ({ elementState: n }, e) => { - const { value: s, selection: t } = n; - if (!s || Me(s, t)) - return { elementState: n }; - const [i, r] = t, l = s.slice(i, r).replaceAll(/\d/g, "0"), a = s.slice(0, i) + l + s.slice(r); - return e === "validation" || e === "insert" && i === r ? { - elementState: { selection: t, value: a } - } : { - elementState: { - selection: e === "deleteBackward" || e === "insert" ? [i, i] : [r, r], - value: a - } - }; - }; -} -function Me(n, [e, s]) { - return s === n.length; -} -function Oe({ mode: n, separator: e = ".", max: s, min: t }) { - const i = n.split("/").join(e); - return Object.assign(Object.assign({}, I), { mask: Array.from(i).map((r) => e.includes(r) ? r : /\d/), overwriteMode: "replace", preprocessors: [ - xe(), - Ie(), - Ce({ - dateModeTemplate: i, - dateSegmentsSeparator: e - }), - Re({ - dateModeTemplate: i, - dateSegmentsSeparator: e - }) - ], postprocessors: [ - Ae({ - dateModeTemplate: i, - dateSegmentSeparator: e, - splitFn: (r) => ({ dateStrings: [r] }), - uniteFn: ([r]) => r - }), - ke({ - min: t, - max: s, - dateModeTemplate: i, - dateSegmentSeparator: e - }) - ] }); -} -new RegExp(`[${be.map(q).join("")}]$`); -const Fe = /^(?:\d{4}[ -]?){0,3}\d{0,4}$/, p = { - // Visa: Starts with 4, 13 or 16 digits - visa: { - final: /^4(?:\d{3}[- ]?){3}\d{3,4}$/, - // Exactly 13 or 16 digits - start: /^4/, - // Checks if the input starts with 4 - length: /^4\d{0,15}$/ - // Strictly matches 1 to 16 digits after the initial 4, no spaces or dashes - }, - // MasterCard: Starts with 51-55, 16 digits - mastercard: { - final: /^5[1-5]\d{3}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/, - // Exactly 16 digits - start: /^5[1-5]/, - // Checks if the input starts with 51-55 - length: /^5[1-5]\d{0,15}$/ - // Strictly matches 2 to 16 digits after the initial 51-55, no spaces or dashes - }, - // American Express: Starts with 34 or 37, 15 digits - amex: { - final: /^3[47]\d{2}[- ]?\d{6}[- ]?\d{5}$/, - // Exactly 15 digits - start: /^3[47]/, - // Checks if the input starts with 34 or 37 - length: /^3[47]\d{0,15}$/ - // Strictly matches 2 to 15 digits after the initial 34 or 37, no spaces or dashes - }, - // Discover: Starts with 6011 or 65 or 64[4-9], 16 digits - discover: { - final: /^(6011|65|64[4-9])\d{4}[- ]?\d{4}[- ]?\d{4}$/, - // Exactly 16 digits - start: /^(6011|65|64[4-9])/, - // Checks if the input starts with 6011, 65, or 64 followed by 4-9 - length: /^(6011|65|64[4-9])\d{0,15}$/ - // Strictly matches 4 to 16 digits after the initial prefix, no spaces or dashes - }, - // Diners Club: Starts with 30[0-5], 36, 38, or 39, 14 digits - diners: { - final: /^(30[0-5]|36|38|39)\d{4}[- ]?\d{4}[- ]?\d{4}$/, - // Exactly 14 digits - start: /^(30[0-5]|36|38|39)/, - // Checks if the input starts with 30-35, 36, 38, or 39 - length: /^(30[0-5]|36|38|39)\d{0,14}$/ - // Strictly matches 2 to 14 digits after the initial prefix, no spaces or dashes - }, - // JCB: Starts with 2131, 1800, or 35[0-9]{3}, 15 or 16 digits - jcb: { - final: /^(2131|1800|35[0-9]{3})\d{4}[- ]?\d{4}[- ]?\d{4}$/, - // Exactly 15 or 16 digits - start: /^(2131|1800|35[0-9]{3})/, - // Checks if the input starts with 2131, 1800, or 35 followed by 3 digits - length: /^(2131|1800|35[0-9]{3})\d{0,15}$/ - // Strictly matches 4 to 16 digits after the initial prefix, no spaces or dashes - } -}; -var x, z; -class $e { - constructor(e) { - _(this, x); - this.options = e; - } - mount() { - return this.number = this.options.fields.card.number instanceof HTMLInputElement ? this.options.fields.card.number : document.querySelector( - this.options.fields.card.number - ), this.date = this.options.fields.card.date instanceof HTMLInputElement ? this.options.fields.card.date : document.querySelector( - this.options.fields.card.date - ), this.cvv = this.options.fields.card.cvv instanceof HTMLInputElement ? this.options.fields.card.cvv : document.querySelector( - this.options.fields.card.cvv - ), $(this, x, z).call(this), this; - } - check() { - const e = p.visa.final.test(this.number.value) || p.mastercard.final.test(this.number.value) || p.amex.final.test(this.number.value) || p.discover.final.test(this.number.value) || p.diners.final.test(this.number.value) || p.jcb.final.test(this.number.value), s = new RegExp("^(0[1-9]|1[0-2])/(?:\\d{2})$").test( - this.date.value - ), t = new RegExp("^\\d{3}$").test(this.cvv.value); - return { - valid: e && s && t, - number: { - valid: e, - value: this.number.value - }, - date: { - valid: s, - value: this.date.value - }, - cvv: { - valid: t, - value: this.cvv.value - } - }; - } - type() { - return p.visa.start.test(this.number.value) ? "visa" : p.mastercard.start.test(this.number.value) ? "mastercard" : p.amex.start.test(this.number.value) ? "amex" : p.discover.start.test(this.number.value) ? "discover" : p.diners.start.test(this.number.value) ? "diners" : p.jcb.start.test(this.number.value) ? "jcb" : "unknown"; - } -} -x = new WeakSet(), z = function() { - new C(this.number, { - mask: (e) => p.visa.start.test(e.value) ? new RegExp(p.visa.length) : p.mastercard.start.test(e.value) ? new RegExp(p.mastercard.length) : p.amex.start.test(e.value) ? new RegExp(p.amex.length) : p.discover.start.test(e.value) ? new RegExp(p.discover.length) : p.diners.start.test(e.value) ? new RegExp(p.diners.length) : p.jcb.start.test(e.value) ? new RegExp(p.jcb.length) : new RegExp(Fe) - }), new C( - this.date, - Oe({ - mode: "mm/yy", - separator: "/" - }) - ), new C(this.cvv, { - mask: [/\d/, /\d/, /\d/] - }); -}; -export { - $e as SimpleCard, - p as masks, - Fe as numbers -}; diff --git a/resources/js/clients/payment_methods/authorize-authorize-card.js b/resources/js/clients/payment_methods/authorize-authorize-card.js index 2c92b5743e9a..ad025c633493 100644 --- a/resources/js/clients/payment_methods/authorize-authorize-card.js +++ b/resources/js/clients/payment_methods/authorize-authorize-card.js @@ -15,34 +15,49 @@ class AuthorizeAuthorizeCard { this.loginId = loginId; this.cardHolderName = document.getElementById("cardholder_name"); this.cardButton = document.getElementById("card_button"); + + this.sc = createSimpleCard({ + fields: { + card: { + number: '#number', + date: '#date', + cvv: '#cvv', + }, + }, + }); + + this.sc.mount(); } handleAuthorization() { + if ( + this.cvvRequired == '1' && + document.getElementById('cvv').value.length < 3 + ) { + const $errors = document.getElementById('errors'); + + if ($errors) { + $errors.innerText = 'CVV is required'; + $errors.style.display = 'block'; + } - - if (cvvRequired == "1" && document.getElementById("cvv").value.length < 3) { - - var $errors = $('#errors'); - $errors.show().html("

CVV is required

"); - - document.getElementById('card_button').disabled = false; - document.querySelector('#card_button > svg').classList.add('hidden'); - document.querySelector('#card_button > span').classList.remove('hidden'); - + document.getElementById('pay-now').disabled = false; + document.querySelector('#pay-now > svg').classList.add('hidden'); + document + .querySelector('#pay-now > span') + .classList.remove('hidden'); return; } - var myCard = $('#my-card'); - var authData = {}; authData.clientKey = this.publicKey; authData.apiLoginID = this.loginId; var cardData = {}; - cardData.cardNumber = myCard.CardJs('cardNumber').replace(/[^\d]/g, ''); - cardData.month = myCard.CardJs('expiryMonth').replace(/[^\d]/g, ''); - cardData.year = myCard.CardJs('expiryYear').replace(/[^\d]/g, ''); - cardData.cardCode = document.getElementById("cvv").value.replace(/[^\d]/g, '');; + cardData.cardNumber = this.sc.value('number')?.replace(/[^\d]/g, ''); + cardData.month = this.sc.value('month')?.replace(/[^\d]/g, ''); + cardData.year = `20${this.sc.value('year')?.replace(/[^\d]/g, '')}`; + cardData.cardCode = this.sc.value('cvv')?.replace(/[^\d]/g, ''); var secureData = {}; secureData.authData = authData; @@ -61,8 +76,12 @@ class AuthorizeAuthorizeCard { if (response.messages.resultCode === "Error") { var i = 0; - var $errors = $('#errors'); // get the reference of the div - $errors.show().html("

" + response.messages.message[i].code + ": " + response.messages.message[i].text + "

"); + const $errors = document.getElementById('errors'); // get the reference of the div + + if ($errors) { + $errors.innerText = `${response.messages.message[i].code}: ${response.messages.message[i].text}`; + $errors.style.display = 'block'; + } document.getElementById('card_button').disabled = false; document.querySelector('#card_button > svg').classList.add('hidden'); diff --git a/resources/js/clients/payment_methods/braintree-ach.js b/resources/js/clients/payment_methods/braintree-ach.js index e646c8de0ee3..aa4f4e1cc5a4 100644 --- a/resources/js/clients/payment_methods/braintree-ach.js +++ b/resources/js/clients/payment_methods/braintree-ach.js @@ -8,61 +8,67 @@ * @license https://www.elastic.co/licensing/elastic-license */ -window.braintree.client.create({ - authorization: document.querySelector('meta[name="client-token"]')?.content -}).then(function (clientInstance) { - return braintree.usBankAccount.create({ - client: clientInstance - }); -}).then(function (usBankAccountInstance) { - document - .getElementById('authorize-bank-account') - ?.addEventListener('click', (e) => { - e.target.parentElement.disabled = true; +import { instant, wait } from '../wait'; - document.getElementById('errors').hidden = true; - document.getElementById('errors').textContent = ''; - - let bankDetails = { - accountNumber: document.getElementById('account-number').value, - routingNumber: document.getElementById('routing-number').value, - accountType: document.querySelector('input[name="account-type"]:checked').value, - ownershipType: document.querySelector('input[name="ownership-type"]:checked').value, - billingAddress: { - streetAddress: document.getElementById('billing-street-address').value, - extendedAddress: document.getElementById('billing-extended-address').value, - locality: document.getElementById('billing-locality').value, - region: document.getElementById('billing-region').value, - postalCode: document.getElementById('billing-postal-code').value - } - } - - if (bankDetails.ownershipType === 'personal') { - let name = document.getElementById('account-holder-name').value.split(' ', 2); - - bankDetails.firstName = name[0]; - bankDetails.lastName = name[1]; - } else { - bankDetails.businessName = document.getElementById('account-holder-name').value; - } - - usBankAccountInstance.tokenize({ - bankDetails, - mandateText: 'By clicking ["Checkout"], I authorize Braintree, a service of PayPal, on behalf of [your business name here] (i) to verify my bank account information using bank information and consumer reports and (ii) to debit my bank account.' - }).then(function (payload) { - document.querySelector('input[name=nonce]').value = payload.nonce; - document.getElementById('server_response').submit(); - }) - .catch(function (error) { - e.target.parentElement.disabled = false; - - document.getElementById('errors').textContent = `${error.details.originalError.message} ${error.details.originalError.details.originalError[0].message}`; - document.getElementById('errors').hidden = false; - }); +function boot() { + window.braintree.client.create({ + authorization: document.querySelector('meta[name="client-token"]')?.content + }).then(function (clientInstance) { + return braintree.usBankAccount.create({ + client: clientInstance }); -}).catch(function (err) { + }).then(function (usBankAccountInstance) { + document + .getElementById('authorize-bank-account') + ?.addEventListener('click', (e) => { + e.target.parentElement.disabled = true; + + document.getElementById('errors').hidden = true; + document.getElementById('errors').textContent = ''; + + let bankDetails = { + accountNumber: document.getElementById('account-number').value, + routingNumber: document.getElementById('routing-number').value, + accountType: document.querySelector('input[name="account-type"]:checked').value, + ownershipType: document.querySelector('input[name="ownership-type"]:checked').value, + billingAddress: { + streetAddress: document.getElementById('billing-street-address').value, + extendedAddress: document.getElementById('billing-extended-address').value, + locality: document.getElementById('billing-locality').value, + region: document.getElementById('billing-region').value, + postalCode: document.getElementById('billing-postal-code').value + } + } + + if (bankDetails.ownershipType === 'personal') { + let name = document.getElementById('account-holder-name').value.split(' ', 2); + + bankDetails.firstName = name[0]; + bankDetails.lastName = name[1]; + } else { + bankDetails.businessName = document.getElementById('account-holder-name').value; + } + + usBankAccountInstance.tokenize({ + bankDetails, + mandateText: 'By clicking ["Checkout"], I authorize Braintree, a service of PayPal, on behalf of [your business name here] (i) to verify my bank account information using bank information and consumer reports and (ii) to debit my bank account.' + }).then(function (payload) { + document.querySelector('input[name=nonce]').value = payload.nonce; + document.getElementById('server_response').submit(); + }) + .catch(function (error) { + e.target.parentElement.disabled = false; + + document.getElementById('errors').textContent = `${error.details.originalError.message} ${error.details.originalError.details.originalError[0].message}`; + document.getElementById('errors').hidden = false; + }); + }); + }).catch(function (err) { + + document.getElementById('errors').textContent = err.message; + document.getElementById('errors').hidden = false; + + }); +} - document.getElementById('errors').textContent = err.message; - document.getElementById('errors').hidden = false; - -}); +instant() ? boot() : wait('#braintree-ach-authorize').then(() => boot()); diff --git a/resources/js/clients/payments/authorize-credit-card-payment.js b/resources/js/clients/payments/authorize-credit-card-payment.js index 2d4ec69b3546..1c2c8d88b6a4 100644 --- a/resources/js/clients/payments/authorize-credit-card-payment.js +++ b/resources/js/clients/payments/authorize-credit-card-payment.js @@ -5,40 +5,64 @@ * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license + * @license https://www.elastic.co/licensing/elastic-license */ -class AuthorizeAuthorizeCard { +import { wait, instant } from '../wait'; +class AuthorizeAuthorizeCard { constructor(publicKey, loginId) { this.publicKey = publicKey; this.loginId = loginId; - this.cardHolderName = document.getElementById("cardholder_name"); + this.cardHolderName = document.getElementById('cardholder_name'); + + this.sc = createSimpleCard({ + fields: { + card: { + number: '#number', + date: '#date', + cvv: '#cvv', + }, + }, + }); + + this.sc.mount(); + + this.cvvRequired = document.querySelector( + 'meta[name="authnet-require-cvv"]' + ).content; + } handleAuthorization = () => { + if ( + this.cvvRequired == '1' && + document.getElementById('cvv').value.length < 3 + ) { + const $errors = document.getElementById('errors'); - if (cvvRequired == "1" && document.getElementById("cvv").value.length < 3) { - var $errors = $('#errors'); - $errors.show().html("

CVV is required

"); + if ($errors) { + $errors.innerText = 'CVV is required'; + $errors.style.display = 'block'; + } document.getElementById('pay-now').disabled = false; document.querySelector('#pay-now > svg').classList.add('hidden'); - document.querySelector('#pay-now > span').classList.remove('hidden'); + document + .querySelector('#pay-now > span') + .classList.remove('hidden'); return; } - var myCard = $('#my-card'); - var authData = {}; authData.clientKey = this.publicKey; authData.apiLoginID = this.loginId; var cardData = {}; - cardData.cardNumber = myCard.CardJs('cardNumber').replace(/[^\d]/g, ''); - cardData.month = myCard.CardJs('expiryMonth').replace(/[^\d]/g, ''); - cardData.year = myCard.CardJs('expiryYear').replace(/[^\d]/g, ''); - cardData.cardCode = document.getElementById("cvv").value.replace(/[^\d]/g, ''); + cardData.cardNumber = this.sc.value('number')?.replace(/[^\d]/g, ''); + cardData.month = this.sc.value('month')?.replace(/[^\d]/g, ''); + cardData.year = `20${this.sc.value('year')?.replace(/[^\d]/g, '')}`; + cardData.cardCode = this.sc.value('cvv')?.replace(/[^\d]/g, ''); var secureData = {}; secureData.authData = authData; @@ -58,102 +82,111 @@ class AuthorizeAuthorizeCard { Accept.dispatchData(secureData, this.responseHandler); return false; - - } + }; handlePayNowAction(token_hashed_id) { document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); document.querySelector('#pay-now > span').classList.add('hidden'); - document.getElementById("token").value = token_hashed_id; - document.getElementById("server_response").submit(); + document.getElementById('token').value = token_hashed_id; + document.getElementById('server_response').submit(); } responseHandler = (response) => { - if (response.messages.resultCode === "Error") { + if (response.messages.resultCode === 'Error') { var i = 0; - var $errors = $('#errors'); // get the reference of the div - $errors.show().html("

" + response.messages.message[i].code + ": " + response.messages.message[i].text + "

"); + const $errors = document.getElementById('errors'); // get the reference of the div + + if ($errors) { + $errors.innerText = `${response.messages.message[i].code}: ${response.messages.message[i].text}`; + $errors.style.display = 'block'; + } document.getElementById('pay-now').disabled = false; document.querySelector('#pay-now > svg').classList.add('hidden'); - document.querySelector('#pay-now > span').classList.remove('hidden'); - } else if (response.messages.resultCode === "Ok") { + document + .querySelector('#pay-now > span') + .classList.remove('hidden'); + } else if (response.messages.resultCode === 'Ok') { + document.getElementById('dataDescriptor').value = + response.opaqueData.dataDescriptor; + document.getElementById('dataValue').value = + response.opaqueData.dataValue; - document.getElementById("dataDescriptor").value = response.opaqueData.dataDescriptor; - document.getElementById("dataValue").value = response.opaqueData.dataValue; - - let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked'); + let storeCard = document.querySelector( + 'input[name=token-billing-checkbox]:checked' + ); if (storeCard) { - document.getElementById("store_card").value = storeCard.value; + document.getElementById('store_card').value = storeCard.value; } - document.getElementById("server_response").submit(); + document.getElementById('server_response').submit(); } return false; - } - + }; handle = () => { - Array - .from(document.getElementsByClassName('toggle-payment-with-token')) - .forEach((element) => element.addEventListener('click', (e) => { - document - .getElementById('save-card--container').style.display = 'none'; - document - .getElementById('authorize--credit-card-container').style.display = 'none'; + Array.from( + document.getElementsByClassName('toggle-payment-with-token') + ).forEach((element) => + element.addEventListener('click', (e) => { + document.getElementById('save-card--container').style.display = + 'none'; + document.getElementById( + 'authorize--credit-card-container' + ).style.display = 'none'; - document - .getElementById('token').value = e.target.dataset.token; - })); + document.getElementById('token').value = e.target.dataset.token; + }) + ); - let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card'); + let payWithCreditCardToggle = document.getElementById( + 'toggle-payment-with-credit-card' + ); if (payWithCreditCardToggle) { - payWithCreditCardToggle - .addEventListener('click', () => { - document - .getElementById('save-card--container').style.display = 'grid'; - document - .getElementById('authorize--credit-card-container').style.display = 'flex'; + payWithCreditCardToggle.addEventListener('click', () => { + document.getElementById('save-card--container').style.display = + 'grid'; + document.getElementById( + 'authorize--credit-card-container' + ).style.display = 'flex'; - document - .getElementById('token').value = null; - }); + document.getElementById('token').value = null; + }); } let payNowButton = document.getElementById('pay-now'); if (payNowButton) { - payNowButton - .addEventListener('click', (e) => { - let token = document.getElementById('token'); + payNowButton.addEventListener('click', (e) => { + let token = document.getElementById('token'); - token.value - ? this.handlePayNowAction(token.value) - : this.handleAuthorization(); - }); + token.value + ? this.handlePayNowAction(token.value) + : this.handleAuthorization(); + }); } return this; - } + }; } -const publicKey = document.querySelector( - 'meta[name="authorize-public-key"]' -).content; +function boot() { + const publicKey = document.querySelector( + 'meta[name="authorize-public-key"]' + ).content; -const loginId = document.querySelector( - 'meta[name="authorize-login-id"]' -).content; + const loginId = document.querySelector( + 'meta[name="authorize-login-id"]' + ).content; -const cvvRequired = document.querySelector( - 'meta[name="authnet-require-cvv"]' -).content; + /** @handle */ + new AuthorizeAuthorizeCard(publicKey, loginId).handle(); +} -/** @handle */ -new AuthorizeAuthorizeCard(publicKey, loginId).handle(); +instant() ? boot() : wait('#authorize-net-credit-card-payment').then(() => boot()); diff --git a/resources/js/clients/payments/braintree-credit-card.js b/resources/js/clients/payments/braintree-credit-card.js index 1c7dacac0489..b959706dc952 100644 --- a/resources/js/clients/payments/braintree-credit-card.js +++ b/resources/js/clients/payments/braintree-credit-card.js @@ -8,6 +8,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; class BraintreeCreditCard { initBraintreeDataCollector() { @@ -43,40 +44,39 @@ class BraintreeCreditCard { } let payNow = document.getElementById('pay-now'); - - params = JSON.parse(document.querySelector('input[name=threeds]').value); + let params = JSON.parse(document.querySelector('input[name=threeds]').value); payNow.addEventListener('click', () => { dropinInstance.requestPaymentMethod({ threeDSecure: { - challengeRequested:true, - amount: params.amount, - email: params.email, - billingAddress: { - givenName: params.billingAddress.givenName, // ASCII-printable characters required, else will throw a validation error - surname: params.billingAddress.surname, // ASCII-printable characters required, else will throw a validation error - phoneNumber: params.billingAddress.phoneNumber, - streetAddress: params.billingAddress.streetAddress, - extendedAddress: params.billingAddress.extendedAddress, - locality: params.billingAddress.locality, - region: params.billingAddress.region, - postalCode: params.billingAddress.postalCode, - countryCodeAlpha2: params.billingAddress.countryCodeAlpha2 - } + challengeRequested: true, + amount: params.amount, + email: params.email, + billingAddress: { + givenName: params.billingAddress.givenName, // ASCII-printable characters required, else will throw a validation error + surname: params.billingAddress.surname, // ASCII-printable characters required, else will throw a validation error + phoneNumber: params.billingAddress.phoneNumber, + streetAddress: params.billingAddress.streetAddress, + extendedAddress: params.billingAddress.extendedAddress, + locality: params.billingAddress.locality, + region: params.billingAddress.region, + postalCode: params.billingAddress.postalCode, + countryCodeAlpha2: params.billingAddress.countryCodeAlpha2 + } + } + }, function (err, payload) { + if (err) { + console.log(err); + dropin.clearSelectedPaymentMethod(); + alert("There was a problem verifying this card, please contact your merchant"); + return; + } + + if (document.querySelector('input[name=threeds_enable]').value === 'true' && !payload.liabilityShifted) { + console.log('Liability did not shift', payload); + alert("There was a problem verifying this card, please contact your merchant"); + return; } - }, function(err, payload) { - if (err) { - console.log(err); - dropin.clearSelectedPaymentMethod(); - alert("There was a problem verifying this card, please contact your merchant"); - return; - } - - if (document.querySelector('input[name=threeds_enable]').value === 'true' && !payload.liabilityShifted) { - console.log('Liability did not shift', payload); - alert("There was a problem verifying this card, please contact your merchant"); - return; - } payNow.disabled = true; @@ -138,4 +138,8 @@ class BraintreeCreditCard { } } -new BraintreeCreditCard().handle(); +function boot() { + new BraintreeCreditCard().handle(); +} + +instant() ? boot() : wait('#braintree-credit-card-payment', 'meta[name=client-token]').then(() => boot()); diff --git a/resources/js/clients/payments/braintree-paypal.js b/resources/js/clients/payments/braintree-paypal.js index affaf1cb26b2..4f0a4965fd5b 100644 --- a/resources/js/clients/payments/braintree-paypal.js +++ b/resources/js/clients/payments/braintree-paypal.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class BraintreePayPal { initBraintreeDataCollector() { window.braintree.client.create({ @@ -86,6 +88,9 @@ class BraintreePayPal { onApprove: function (data, actions) { return paypalCheckoutInstance.tokenizePayment(data).then(function (payload) { + document.querySelector('#paypal-button')?.classList.add('hidden'); + document.querySelector('#paypal-spinner')?.classList.remove('hidden'); + let tokenBillingCheckbox = document.querySelector( 'input[name="token-billing-checkbox"]:checked' ); @@ -119,4 +124,8 @@ class BraintreePayPal { } } -new BraintreePayPal().handle(); +function boot() { + new BraintreePayPal().handle(); +} + +instant() ? boot() : wait('#braintree-paypal-payment').then(() => boot()); diff --git a/resources/js/clients/payments/checkout-credit-card.js b/resources/js/clients/payments/checkout-credit-card.js index bb32fa7337ce..ed07906efd48 100644 --- a/resources/js/clients/payments/checkout-credit-card.js +++ b/resources/js/clients/payments/checkout-credit-card.js @@ -5,64 +5,70 @@ * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license + * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class CheckoutCreditCard { constructor() { this.tokens = []; } - mountFrames() { - console.log('Mount checkout frames..'); - } - handlePaymentUsingToken(e) { document.getElementById('checkout--container').classList.add('hidden'); - document.getElementById('pay-now-with-token--container').classList.remove('hidden'); + document + .getElementById('pay-now-with-token--container') + .classList.remove('hidden'); document.getElementById('save-card--container').style.display = 'none'; - document - .querySelector('input[name=token]') - .value = e.target.dataset.token; + document.querySelector('input[name=token]').value = + e.target.dataset.token; } handlePaymentUsingCreditCard(e) { - document.getElementById('checkout--container').classList.remove('hidden'); - document.getElementById('pay-now-with-token--container').classList.add('hidden'); + document + .getElementById('checkout--container') + .classList.remove('hidden'); + document + .getElementById('pay-now-with-token--container') + .classList.add('hidden'); document.getElementById('save-card--container').style.display = 'grid'; - document - .querySelector('input[name=token]') - .value = ''; + document.querySelector('input[name=token]').value = ''; const payButton = document.getElementById('pay-button'); - - const publicKey = document.querySelector('meta[name="public-key"]').content ?? ''; + + const publicKey = + document.querySelector('meta[name="public-key"]').content ?? ''; const form = document.getElementById('payment-form'); Frames.init(publicKey); - Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED, function (event) { - payButton.disabled = !Frames.isCardValid(); - }); + Frames.addEventHandler( + Frames.Events.CARD_VALIDATION_CHANGED, + function (event) { + payButton.disabled = !Frames.isCardValid(); + } + ); - Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED, function (event) { - payButton.disabled = false; - }); + Frames.addEventHandler( + Frames.Events.CARD_TOKENIZATION_FAILED, + function (event) { + payButton.disabled = false; + } + ); Frames.addEventHandler(Frames.Events.CARD_TOKENIZED, function (event) { payButton.disabled = true; - document.querySelector( - 'input[name="gateway_response"]' - ).value = JSON.stringify(event); + document.querySelector('input[name="gateway_response"]').value = + JSON.stringify(event); - document.querySelector( - 'input[name="store_card"]' - ).value = document.querySelector( - 'input[name=token-billing-checkbox]:checked' - ).value; + document.querySelector('input[name="store_card"]').value = + document.querySelector( + 'input[name=token-billing-checkbox]:checked' + ).value; document.getElementById('server-response').submit(); }); @@ -87,9 +93,11 @@ class CheckoutCreditCard { handle() { this.handlePaymentUsingCreditCard(); - Array - .from(document.getElementsByClassName('toggle-payment-with-token')) - .forEach((element) => element.addEventListener('click', this.handlePaymentUsingToken)); + Array.from( + document.getElementsByClassName('toggle-payment-with-token') + ).forEach((element) => + element.addEventListener('click', this.handlePaymentUsingToken) + ); document .getElementById('toggle-payment-with-credit-card') @@ -101,4 +109,10 @@ class CheckoutCreditCard { } } -new CheckoutCreditCard().handle(); +function boot() { + new CheckoutCreditCard().handle() +} + +instant() ? boot() : wait('#checkout-credit-card-payment').then(() => + new CheckoutCreditCard().handle() +); diff --git a/resources/js/clients/payments/eway-credit-card.js b/resources/js/clients/payments/eway-credit-card.js index 3d4f99297e70..c4ba16b2d14c 100644 --- a/resources/js/clients/payments/eway-credit-card.js +++ b/resources/js/clients/payments/eway-credit-card.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class EwayRapid { constructor() { this.cardStyles = @@ -431,6 +433,11 @@ class EwayRapid { completeAuthorization(event) { event.target.parentElement.disabled = true; + const button = document.getElementById('authorize-card'); + + button.querySelector('svg').classList.remove('hidden'); + button.querySelector('span').classList.add('hidden'); + document.getElementById('server-response').submit(); } @@ -483,8 +490,7 @@ class EwayRapid { }) ); - if (document.getElementById('toggle-payment-with-credit-card')) - { + if (document.getElementById('toggle-payment-with-credit-card')) { document .getElementById('toggle-payment-with-credit-card') .addEventListener('click', (element) => { @@ -498,9 +504,14 @@ class EwayRapid { }); } + const payNowButton = document.getElementById('pay-now'); + document.getElementById('pay-now')?.addEventListener('click', (e) => { let tokenInput = document.querySelector('input[name=token]'); + payNowButton.querySelector('svg').classList.remove('hidden'); + payNowButton.querySelector('span').classList.add('hidden'); + if (tokenInput.value) { return this.completePaymentUsingToken(e); } @@ -510,4 +521,8 @@ class EwayRapid { } } -new EwayRapid().handle(); +function boot() { + new EwayRapid().handle(); +} + +instant() ? boot() : wait('#eway-credit-card-payment').then(() => boot()); diff --git a/resources/js/clients/payments/forte-ach-payment.js b/resources/js/clients/payments/forte-ach-payment.js index 90bf9f675377..8e50c5e2a2ba 100644 --- a/resources/js/clients/payments/forte-ach-payment.js +++ b/resources/js/clients/payments/forte-ach-payment.js @@ -8,6 +8,8 @@ * @license https://opensource.org/licenses/AAL */ +import { wait, instant } from '../wait'; + class ForteAuthorizeACH { constructor(apiLoginId) { this.apiLoginId = apiLoginId; @@ -73,9 +75,13 @@ class ForteAuthorizeACH { }; } -const apiLoginId = document.querySelector( - 'meta[name="forte-api-login-id"]' -).content; +function boot() { + const apiLoginId = document.querySelector( + 'meta[name="forte-api-login-id"]' + ).content; + + /** @handle */ + new ForteAuthorizeACH(apiLoginId).handle(); +} -/** @handle */ -new ForteAuthorizeACH(apiLoginId).handle(); +instant() ? boot() : wait('#force-ach-payment').then(() => boot()); diff --git a/resources/js/clients/payments/forte-credit-card-payment.js b/resources/js/clients/payments/forte-credit-card-payment.js index e406d2153911..c06fc7396793 100644 --- a/resources/js/clients/payments/forte-credit-card-payment.js +++ b/resources/js/clients/payments/forte-credit-card-payment.js @@ -8,21 +8,33 @@ * @license https://opensource.org/licenses/AAL */ +import { wait, instant } from '../wait'; + class ForteAuthorizeCard { constructor(apiLoginId) { this.apiLoginId = apiLoginId; this.cardHolderName = document.getElementById('cardholder_name'); + + this.sc = createSimpleCard({ + fields: { + card: { + number: '#number', + date: '#date', + cvv: '#cvv', + }, + }, + }); + + this.sc.mount(); } handleAuthorization = () => { - var myCard = $('#my-card'); - - var data = { + const data = { api_login_id: this.apiLoginId, - card_number: myCard.CardJs('cardNumber').replace(/[^\d]/g, ''), - expire_year: myCard.CardJs('expiryYear').replace(/[^\d]/g, ''), - expire_month: myCard.CardJs('expiryMonth').replace(/[^\d]/g, ''), - cvv: document.getElementById('cvv').value.replace(/[^\d]/g, ''), + card_number: this.sc.value('number')?.replace(/[^\d]/g, ''), + expire_year: `20${this.sc.value('year')?.replace(/[^\d]/g, '')}`, + expire_month: this.sc.value('month')?.replace(/[^\d]/g, ''), + cvv: this.sc.value('cvv')?.replace(/[^\d]/g, ''), }; let payNowButton = document.getElementById('pay-now'); @@ -41,18 +53,8 @@ class ForteAuthorizeCard { }; successResponseHandler = (response) => { - document.getElementById('payment_token').value = response.onetime_token; - document.getElementById('card_brand').value = response.card_brand; - document.getElementById('expire_year').value = response.expire_year; - document.getElementById('expire_month').value = response.expire_month; - document.getElementById('last_4').value = response.last_4; - - let storeCard = document.querySelector('input[name=token-billing-checkbox]:checked'); - - if (storeCard) { - document.getElementById("store_card").value = storeCard.value; - } + document.getElementById('card_brand').value = response.card_type; document.getElementById('server_response').submit(); @@ -72,99 +74,26 @@ class ForteAuthorizeCard { return false; }; - - handlePayNowAction(token_hashed_id) { - document.getElementById('pay-now').disabled = true; - document.querySelector('#pay-now > svg').classList.remove('hidden'); - document.querySelector('#pay-now > span').classList.add('hidden'); - - document.getElementById("token").value = token_hashed_id; - document.getElementById("server_response").submit(); - } - - handle = () => { - Array - .from(document.getElementsByClassName('toggle-payment-with-token')) - .forEach((element) => element.addEventListener('click', (e) => { - document - .getElementById('save-card--container').style.display = 'none'; - document - .getElementById('forte--credit-card-container').style.display = 'none'; - - document - .getElementById('token').value = e.target.dataset.token; - })); - - let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card'); - - if (payWithCreditCardToggle) { - payWithCreditCardToggle - .addEventListener('click', () => { - document - .getElementById('save-card--container').style.display = 'grid'; - document - .getElementById('forte--credit-card-container').style.display = 'flex'; - - document - .getElementById('token').value = null; - }); - } - let payNowButton = document.getElementById('pay-now'); if (payNowButton) { - payNowButton - .addEventListener('click', (e) => { - let token = document.getElementById('token'); - - token.value - ? this.handlePayNowAction(token.value) - : this.handleAuthorization(); - }); + payNowButton.addEventListener('click', (e) => { + this.handleAuthorization(); + }); } return this; - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + }; } -const apiLoginId = document.querySelector( - 'meta[name="forte-api-login-id"]' -).content; +function boot() { + const apiLoginId = document.querySelector( + 'meta[name="forte-api-login-id"]' + ).content; -/** @handle */ -new ForteAuthorizeCard(apiLoginId).handle(); + /** @handle */ + new ForteAuthorizeCard(apiLoginId).handle(); +} + +instant() ? boot() : wait('#forte-credit-card-payment').then(() => boot()); diff --git a/resources/js/clients/payments/mollie-credit-card.js b/resources/js/clients/payments/mollie-credit-card.js index c973f1147c58..e81a2da34dbf 100644 --- a/resources/js/clients/payments/mollie-credit-card.js +++ b/resources/js/clients/payments/mollie-credit-card.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class _Mollie { constructor() { this.mollie = Mollie( @@ -26,7 +28,7 @@ class _Mollie { let cardHolderError = document.getElementById('card-holder-error'); - cardHolder.addEventListener('change', function(event) { + cardHolder.addEventListener('change', function (event) { if (event.error && event.touched) { cardHolderError.textContent = event.error; } else { @@ -43,7 +45,7 @@ class _Mollie { let cardNumberError = document.getElementById('card-number-error'); - cardNumber.addEventListener('change', function(event) { + cardNumber.addEventListener('change', function (event) { if (event.error && event.touched) { cardNumberError.textContent = event.error; } else { @@ -60,7 +62,7 @@ class _Mollie { let expiryDateError = document.getElementById('expiry-date-error'); - expiryDate.addEventListener('change', function(event) { + expiryDate.addEventListener('change', function (event) { if (event.error && event.touched) { expiryDateError.textContent = event.error; } else { @@ -77,7 +79,7 @@ class _Mollie { let verificationCodeError = document.getElementById('cvv-error'); - verificationCode.addEventListener('change', function(event) { + verificationCode.addEventListener('change', function (event) { if (event.error && event.touched) { verificationCodeError.textContent = event.error; } else { @@ -97,7 +99,7 @@ class _Mollie { return document.getElementById('server-response').submit(); } - this.mollie.createToken().then(function(result) { + this.mollie.createToken().then(function (result) { let token = result.token; let error = result.error; @@ -166,4 +168,9 @@ class _Mollie { } } -new _Mollie().handle(); + +function boot() { + new _Mollie().handle(); +} + +instant() ? boot(): wait('#mollie-credit-card-payment').then(() => boot()); diff --git a/resources/js/clients/payments/paytrace-credit-card.js b/resources/js/clients/payments/paytrace-credit-card.js index 9b67b1ed8d07..7d639bd2a3b1 100644 --- a/resources/js/clients/payments/paytrace-credit-card.js +++ b/resources/js/clients/payments/paytrace-credit-card.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { instant, wait } from '../wait'; + class PayTraceCreditCard { constructor() { this.clientKey = document.querySelector( @@ -122,6 +124,11 @@ class PayTraceCreditCard { } handlePaymentWithCreditCard(event) { + const button = document.getElementById('pay-now'); + + button.querySelector('svg').classList.remove('hidden'); + button.querySelector('span').classList.add('hidden'); + event.target.parentElement.disabled = true; document.getElementById('errors').hidden = true; @@ -132,6 +139,10 @@ class PayTraceCreditCard { errorsContainer.textContent = errors[0].description; errorsContainer.hidden = false; + + button.querySelector('svg').classList.add('hidden'); + button.querySelector('span').classList.remove('hidden'); + return (event.target.parentElement.disabled = false); } @@ -161,6 +172,9 @@ class PayTraceCreditCard { ).textContent = JSON.stringify(error); document.getElementById('errors').hidden = false; + button.querySelector('svg').classList.add('hidden'); + button.querySelector('span').classList.remove('hidden'); + console.log(error); }); }); @@ -169,11 +183,16 @@ class PayTraceCreditCard { handlePaymentWithToken(event) { event.target.parentElement.disabled = true; + const button = document.getElementById('pay-now'); + + button.querySelector('svg').classList.remove('hidden'); + button.querySelector('span').classList.add('hidden'); + document.getElementById('server_response').submit(); } handle() { - + Array.from( document.getElementsByClassName('toggle-payment-with-token') ).forEach((element) => @@ -217,7 +236,15 @@ class PayTraceCreditCard { return this.handlePaymentWithToken(e); }); + + if (Array.from(document.getElementsByClassName('toggle-payment-with-token')).length === 0 && !instant()) { + document.getElementById('toggle-payment-with-credit-card').click(); + } } } -new PayTraceCreditCard().handle(); +function boot() { + new PayTraceCreditCard().handle(); +} + +instant() ? boot() : wait('#paytrace-credit-card-payment').then(() => boot()) diff --git a/resources/js/clients/payments/razorpay-aio.js b/resources/js/clients/payments/razorpay-aio.js index 4097b07fcbf9..42e8ae1313d1 100644 --- a/resources/js/clients/payments/razorpay-aio.js +++ b/resources/js/clients/payments/razorpay-aio.js @@ -8,29 +8,35 @@ * @license https://www.elastic.co/licensing/elastic-license */ -let options = JSON.parse( - document.querySelector('meta[name=razorpay-options]')?.content -); +import { wait, instant } from '../wait'; -options.handler = function (response) { - document.getElementById('razorpay_payment_id').value = - response.razorpay_payment_id; - document.getElementById('razorpay_signature').value = - response.razorpay_signature; - document.getElementById('server-response').submit(); -}; +function boot() { + let options = JSON.parse( + document.querySelector('meta[name=razorpay-options]')?.content + ); + + options.handler = function (response) { + document.getElementById('razorpay_payment_id').value = + response.razorpay_payment_id; + document.getElementById('razorpay_signature').value = + response.razorpay_signature; + document.getElementById('server-response').submit(); + }; + + options.modal = { + ondismiss: function () { + payNowButton.disabled = false; + }, + }; + + let razorpay = new Razorpay(options); + let payNowButton = document.getElementById('pay-now'); + + payNowButton.onclick = function (event) { + payNowButton.disabled = true; + + razorpay.open(); + }; +} -options.modal = { - ondismiss: function () { - payNowButton.disabled = false; - }, -}; - -let razorpay = new Razorpay(options); -let payNowButton = document.getElementById('pay-now'); - -payNowButton.onclick = function (event) { - payNowButton.disabled = true; - - razorpay.open(); -}; +instant() ? boot() : wait('#razorpay-hosted-payment').then(() => boot()); diff --git a/resources/js/clients/payments/square-credit-card.js b/resources/js/clients/payments/square-credit-card.js index 4242eae5d651..73ea9ea503a3 100644 --- a/resources/js/clients/payments/square-credit-card.js +++ b/resources/js/clients/payments/square-credit-card.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class SquareCreditCard { constructor() { this.appId = document.querySelector('meta[name=square-appId]').content; @@ -130,54 +132,58 @@ class SquareCreditCard { await this.init().then(() => { - document - .getElementById('authorize-card') - ?.addEventListener('click', (e) => - this.completePaymentWithoutToken(e) + document + .getElementById('authorize-card') + ?.addEventListener('click', (e) => + this.completePaymentWithoutToken(e) + ); + + document.getElementById('pay-now')?.addEventListener('click', (e) => { + let tokenInput = document.querySelector('input[name=token]'); + + if (tokenInput.value) { + return this.completePaymentUsingToken(e); + } + + return this.completePaymentWithoutToken(e); + }); + + Array.from( + document.getElementsByClassName('toggle-payment-with-token') + ).forEach((element) => + element.addEventListener('click', async (element) => { + document + .getElementById('card-container') + .classList.add('hidden'); + document.getElementById('save-card--container').style.display = + 'none'; + document.querySelector('input[name=token]').value = + element.target.dataset.token; + }) ); - document.getElementById('pay-now')?.addEventListener('click', (e) => { - let tokenInput = document.querySelector('input[name=token]'); - - if (tokenInput.value) { - return this.completePaymentUsingToken(e); - } - - return this.completePaymentWithoutToken(e); - }); - - Array.from( - document.getElementsByClassName('toggle-payment-with-token') - ).forEach((element) => - element.addEventListener('click', async (element) => { - document - .getElementById('card-container') - .classList.add('hidden'); - document.getElementById('save-card--container').style.display = - 'none'; - document.querySelector('input[name=token]').value = - element.target.dataset.token; - }) - ); - - document - .getElementById('toggle-payment-with-credit-card') - ?.addEventListener('click', async (element) => { - document - .getElementById('card-container') - .classList.remove('hidden'); - document.getElementById('save-card--container').style.display = - 'grid'; - document.querySelector('input[name=token]').value = ''; - }); + document + .getElementById('toggle-payment-with-credit-card') + ?.addEventListener('click', async (element) => { + document + .getElementById('card-container') + .classList.remove('hidden'); + document.getElementById('save-card--container').style.display = + 'grid'; + document.querySelector('input[name=token]').value = ''; + }); document.getElementById('loader').classList.add('hidden'); document.getElementById('payment-list').classList.remove('hidden'); document.getElementById('toggle-payment-with-credit-card')?.click(); - }); + }); } } -new SquareCreditCard().handle(); +function boot() { + new SquareCreditCard().handle(); +} + +instant() ? boot() : wait('#square-credit-card-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-acss.js b/resources/js/clients/payments/stripe-acss.js index 613e8e373497..497a369e27d2 100644 --- a/resources/js/clients/payments/stripe-acss.js +++ b/resources/js/clients/payments/stripe-acss.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { instant, wait } from '../wait'; + class ProcessACSS { constructor(key, stripeConnect) { this.key = key; @@ -17,13 +19,13 @@ class ProcessACSS { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -33,7 +35,6 @@ class ProcessACSS { }; handle = () => { - Array .from(document.getElementsByClassName('toggle-payment-with-token')) .forEach((element) => element.addEventListener('click', (element) => { @@ -41,8 +42,7 @@ class ProcessACSS { console.log(element.target.dataset.token); })); - if(document.getElementById('toggle-payment-with-new-account')) - { + if (document.getElementById('toggle-payment-with-new-account')) { document .getElementById('toggle-payment-with-new-account') .addEventListener('click', (element) => { @@ -52,19 +52,18 @@ class ProcessACSS { } - if (document.getElementById('pay-now-with-token')) - { + if (document.getElementById('pay-now-with-token')) { document.getElementById('pay-now-with-token').addEventListener('click', (e) => { const token = document .querySelector('input[name=token]') .value; - document.getElementById('pay-now-with-token').disabled = true; - document.querySelector('#pay-now-with-token > svg').classList.remove('hidden'); - document.querySelector('#pay-now-with-token > span').classList.add('hidden'); - document.getElementById('server-response').submit(); - + document.getElementById('pay-now-with-token').disabled = true; + document.querySelector('#pay-now-with-token > svg').classList.remove('hidden'); + document.querySelector('#pay-now-with-token > span').classList.add('hidden'); + document.getElementById('server-response').submit(); + }); } else { @@ -80,23 +79,23 @@ class ProcessACSS { tokenBillingCheckbox.value; } - let errors = document.getElementById('errors'); - errors.textContent = ''; - errors.hidden = true; - - if (document.getElementById('acss-name').value === "") { - document.getElementById('acss-name').focus(); - errors.textContent = document.querySelector('meta[name=translation-name-required]').content; - errors.hidden = false; - return; - } + let errors = document.getElementById('errors'); + errors.textContent = ''; + errors.hidden = true; - if (document.getElementById('acss-email-address').value === "") { - document.getElementById('acss-email-address').focus(); - errors.textContent = document.querySelector('meta[name=translation-email-required]').content; - errors.hidden = false; - return ; - } + if (document.getElementById('acss-name').value === "") { + document.getElementById('acss-name').focus(); + errors.textContent = document.querySelector('meta[name=translation-name-required]').content; + errors.hidden = false; + return; + } + + if (document.getElementById('acss-email-address').value === "") { + document.getElementById('acss-email-address').focus(); + errors.textContent = document.querySelector('meta[name=translation-email-required]').content; + errors.hidden = false; + return; + } document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); @@ -139,17 +138,30 @@ class ProcessACSS { errors.textContent = message; errors.hidden = false; - document.getElementById('pay-now').disabled = false; - document.querySelector('#pay-now > svg').classList.add('hidden'); - document.querySelector('#pay-now > span').classList.remove('hidden'); + document.getElementById('pay-now').disabled = false; + document.querySelector('#pay-now > svg').classList.add('hidden'); + document.querySelector('#pay-now > span').classList.remove('hidden'); } } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessACSS(publishableKey, stripeConnect).setupStripe().handle(); -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + /** + * @type {HTMLInputElement|null} + */ + const first = document.querySelector('input[name="payment-type"]'); -new ProcessACSS(publishableKey, stripeConnect).setupStripe().handle(); + if (first) { + first.click(); + } +} + +instant() ? boot() : wait('#stripe-acss-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/js/clients/payments/stripe-alipay.js b/resources/js/clients/payments/stripe-alipay.js index 9b902a7dee35..6a00310fcae9 100644 --- a/resources/js/clients/payments/stripe-alipay.js +++ b/resources/js/clients/payments/stripe-alipay.js @@ -5,9 +5,11 @@ * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license + * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessAlipay { constructor(key, stripeConnect) { this.key = key; @@ -17,57 +19,68 @@ class ProcessAlipay { } setupStripe = () => { + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - - } - else { + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); } - return this; }; async handle() { + document + .getElementById('pay-now') + .addEventListener('click', async (e) => { + document.getElementById('pay-now').disabled = true; + document + .querySelector('#pay-now > svg') + .classList.add('hidden'); + document + .querySelector('#pay-now > span') + .classList.remove('hidden'); - document.getElementById('pay-now').addEventListener('click', async (e) => { - document.getElementById('pay-now').disabled = true; - document.querySelector('#pay-now > svg').classList.add('hidden'); - document.querySelector('#pay-now > span').classList.remove('hidden'); - - const { error } = await this.stripe.confirmAlipayPayment(document.querySelector('meta[name=ci_intent]').content, { - // Return URL where the customer should be redirected after the authorization - return_url: `${document.querySelector('meta[name=return_url]').content}`, - }); + const { error } = await this.stripe.confirmAlipayPayment( + document.querySelector('meta[name=ci_intent]').content, + { + // Return URL where the customer should be redirected after the authorization + return_url: `${ + document.querySelector('meta[name=return_url]') + .content + }`, + } + ); document.getElementById('pay-now').disabled = false; - document.querySelector('#pay-now > svg').classList.remove('hidden'); - document.querySelector('#pay-now > span').classList.add('hidden'); + document + .querySelector('#pay-now > svg') + .classList.remove('hidden'); + document + .querySelector('#pay-now > span') + .classList.add('hidden'); if (error) { - this.errors.textContent = ''; this.errors.textContent = result.error.message; this.errors.hidden = false; - } }); - } } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = + document.querySelector('meta[name="stripe-publishable-key"]') + ?.content ?? ''; -const stripeConnect = document.querySelector( - 'meta[name="stripe-account-id"]' -)?.content ?? ''; + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; -new ProcessAlipay(publishableKey, stripeConnect).setupStripe().handle(); + new ProcessAlipay(publishableKey, stripeConnect).setupStripe().handle(); +} + +instant() ? boot() : wait('#stripe-alipay-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-bacs.js b/resources/js/clients/payments/stripe-bacs.js index a67c9057c588..964698d2d656 100644 --- a/resources/js/clients/payments/stripe-bacs.js +++ b/resources/js/clients/payments/stripe-bacs.js @@ -8,8 +8,10 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { instant, wait } from '../wait'; + class ProcessBACS { - constructor(key, stripeConnect) { + constructor(key, stripeConnect, onlyAuthorization) { this.key = key; this.errors = document.getElementById('errors'); this.stripeConnect = stripeConnect; @@ -18,7 +20,7 @@ class ProcessBACS { setupStripe = () => { - if (this.stripeConnect){ + if (this.stripeConnect) { // this.stripe.stripeAccount = this.stripeConnect; this.stripe = Stripe(this.key, { @@ -42,9 +44,10 @@ class ProcessBACS { document.getElementById('authorize-bacs').disabled = true; document.querySelector('#authorize-bacs > svg').classList.remove('hidden'); document.querySelector('#authorize-bacs > span').classList.add('hidden'); - location.href=document.querySelector('meta[name=stripe-redirect-url]').content; - });} - else{ + location.href = document.querySelector('meta[name=stripe-redirect-url]').content; + }); + } + else { this.payNowButton = document.getElementById('pay-now'); document.getElementById('pay-now').addEventListener('click', (e) => { this.payNowButton.disabled = true; @@ -54,14 +57,15 @@ class ProcessBACS { }); this.payment_data = Array.from(document.getElementsByClassName('toggle-payment-with-token')); - if (this.payment_data.length > 0){ + if (this.payment_data.length > 0) { this.payment_data.forEach((element) => element.addEventListener('click', (element) => { document.querySelector('input[name=token]').value = element.target.dataset.token; }) - );} - else{ + ); + } + else { this.errors.textContent = document.querySelector( 'meta[name=translation-payment-method-required]' ).content; @@ -69,19 +73,33 @@ class ProcessBACS { this.payNowButton.disabled = true; this.payNowButton.querySelector('span').classList.remove('hidden'); this.payNowButton.querySelector('svg').classList.add('hidden'); - }} + } + } } } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + const onlyAuthorization = + document.querySelector('meta[name="only-authorization"]')?.content ?? ''; + + new ProcessBACS(publishableKey, stripeConnect, onlyAuthorization).setupStripe().handle(); -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; -const onlyAuthorization = - document.querySelector('meta[name="only-authorization"]')?.content ?? ''; + /** + * @type {HTMLInputElement|null} + */ + const first = document.querySelector('input[name="payment-type"]'); -new ProcessBACS(publishableKey, stripeConnect).setupStripe().handle(); + if (first) { + first.click(); + } +} + +instant() ? boot() : wait('#stripe-bacs-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-bancontact.js b/resources/js/clients/payments/stripe-bancontact.js index 35860607b775..74ad70cddf46 100644 --- a/resources/js/clients/payments/stripe-bancontact.js +++ b/resources/js/clients/payments/stripe-bancontact.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessBANCONTACTPay { constructor(key, stripeConnect) { this.key = key; @@ -17,13 +19,13 @@ class ProcessBANCONTACTPay { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -40,7 +42,7 @@ class ProcessBANCONTACTPay { errors.textContent = document.querySelector('meta[name=translation-name-required]').content; errors.hidden = false; console.log("name"); - return ; + return; } document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); @@ -63,11 +65,15 @@ class ProcessBANCONTACTPay { }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessBANCONTACTPay(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; - -new ProcessBANCONTACTPay(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-bancontact-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-becs.js b/resources/js/clients/payments/stripe-becs.js index 61544ccf53c7..b463e1014813 100644 --- a/resources/js/clients/payments/stripe-becs.js +++ b/resources/js/clients/payments/stripe-becs.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessBECS { constructor(key, stripeConnect) { this.key = key; @@ -17,13 +19,13 @@ class ProcessBECS { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -64,30 +66,30 @@ class ProcessBECS { handle = () => { document.getElementById('pay-now').addEventListener('click', (e) => { - let errors = document.getElementById('errors'); + let errors = document.getElementById('errors'); - if (document.getElementById('becs-name').value === "") { - document.getElementById('becs-name').focus(); - errors.textContent = document.querySelector('meta[name=translation-name-required]').content; - errors.hidden = false; - return; - } + if (document.getElementById('becs-name').value === "") { + document.getElementById('becs-name').focus(); + errors.textContent = document.querySelector('meta[name=translation-name-required]').content; + errors.hidden = false; + return; + } - if (document.getElementById('becs-email-address').value === "") { - document.getElementById('becs-email-address').focus(); - errors.textContent = document.querySelector('meta[name=translation-email-required]').content; - errors.hidden = false; - return ; - } + if (document.getElementById('becs-email-address').value === "") { + document.getElementById('becs-email-address').focus(); + errors.textContent = document.querySelector('meta[name=translation-email-required]').content; + errors.hidden = false; + return; + } - if (!document.getElementById('becs-mandate-acceptance').checked) { - document.getElementById('becs-mandate-acceptance').focus(); - errors.textContent = document.querySelector('meta[name=translation-terms-required]').content; - errors.hidden = false; - console.log("Terms"); - return ; - } + if (!document.getElementById('becs-mandate-acceptance').checked) { + document.getElementById('becs-mandate-acceptance').focus(); + errors.textContent = document.querySelector('meta[name=translation-terms-required]').content; + errors.hidden = false; + console.log("Terms"); + return; + } document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); @@ -129,17 +131,21 @@ class ProcessBECS { errors.textContent = message; errors.hidden = false; - document.getElementById('pay-now').disabled = false; - document.querySelector('#pay-now > svg').classList.add('hidden'); - document.querySelector('#pay-now > span').classList.remove('hidden'); + document.getElementById('pay-now').disabled = false; + document.querySelector('#pay-now > svg').classList.add('hidden'); + document.querySelector('#pay-now > span').classList.remove('hidden'); } } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessBECS(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; - -new ProcessBECS(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-becs-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-browserpay.js b/resources/js/clients/payments/stripe-browserpay.js index dff741ddc63a..ad53816f4ccd 100644 --- a/resources/js/clients/payments/stripe-browserpay.js +++ b/resources/js/clients/payments/stripe-browserpay.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class StripeBrowserPay { constructor() { this.clientSecret = document.querySelector( @@ -68,7 +70,7 @@ class StripeBrowserPay { ) .then(function (confirmResult) { if (confirmResult.error) { - + document.querySelector('#errors').innerText = confirmResult.error.message; document.querySelector('#errors').hidden = false; @@ -142,4 +144,8 @@ class StripeBrowserPay { } } -new StripeBrowserPay().handle(); +function boot() { + new StripeBrowserPay().handle() +} + +instant() ? boot() : wait('#stripe-browserpay-payment').then(() => boot()) diff --git a/resources/js/clients/payments/stripe-credit-card.js b/resources/js/clients/payments/stripe-credit-card.js index f35d20dcbde2..eb60acde543d 100644 --- a/resources/js/clients/payments/stripe-credit-card.js +++ b/resources/js/clients/payments/stripe-credit-card.js @@ -5,9 +5,11 @@ * * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com) * - * @license https://www.elastic.co/licensing/elastic-license + * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class StripeCreditCard { constructor(key, secret, onlyAuthorization, stripeConnect) { this.key = key; @@ -17,18 +19,14 @@ class StripeCreditCard { } setupStripe() { - - if (this.stripeConnect){ - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - - } - else { + if (this.stripeConnect) { + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); } - + this.elements = this.stripe.elements(); return this; @@ -36,9 +34,13 @@ class StripeCreditCard { createElement() { this.cardElement = this.elements.create('card', { - hidePostalCode: document.querySelector('meta[name=stripe-require-postal-code]')?.content === "0", + hidePostalCode: + document.querySelector('meta[name=stripe-require-postal-code]') + ?.content === '0', value: { - postalCode: document.querySelector('meta[name=client-postal-code]').content, + postalCode: document.querySelector( + 'meta[name=client-postal-code]' + ).content, }, hideIcon: false, }); @@ -90,7 +92,7 @@ class StripeCreditCard { this.stripe .handleCardPayment(this.secret, this.cardElement, { payment_method_data: { - billing_details: {name: cardHolderName.value}, + billing_details: { name: cardHolderName.value }, }, }) .then((result) => { @@ -103,9 +105,8 @@ class StripeCreditCard { } handleSuccess(result) { - document.querySelector( - 'input[name="gateway_response"]' - ).value = JSON.stringify(result.paymentIntent); + document.querySelector('input[name="gateway_response"]').value = + JSON.stringify(result.paymentIntent); let tokenBillingCheckbox = document.querySelector( 'input[name="token-billing-checkbox"]:checked' @@ -145,7 +146,7 @@ class StripeCreditCard { this.stripe .handleCardSetup(this.secret, this.cardElement, { payment_method_data: { - billing_details: {name: cardHolderName.value}, + billing_details: { name: cardHolderName.value }, }, }) .then((result) => { @@ -177,63 +178,76 @@ class StripeCreditCard { return this.handleAuthorization(); }); } else { - Array - .from(document.getElementsByClassName('toggle-payment-with-token')) - .forEach((element) => element.addEventListener('click', (element) => { - document.getElementById('stripe--payment-container').classList.add('hidden'); - document.getElementById('save-card--container').style.display = 'none'; - document.querySelector('input[name=token]').value = element.target.dataset.token; - })); + Array.from( + document.getElementsByClassName('toggle-payment-with-token') + ).forEach((element) => + element.addEventListener('click', (element) => { + document + .getElementById('stripe--payment-container') + .classList.add('hidden'); + document.getElementById( + 'save-card--container' + ).style.display = 'none'; + document.querySelector('input[name=token]').value = + element.target.dataset.token; + }) + ); document .getElementById('toggle-payment-with-credit-card') .addEventListener('click', (element) => { - document.getElementById('stripe--payment-container').classList.remove('hidden'); - document.getElementById('save-card--container').style.display = 'grid'; - document.querySelector('input[name=token]').value = ""; + document + .getElementById('stripe--payment-container') + .classList.remove('hidden'); + document.getElementById( + 'save-card--container' + ).style.display = 'grid'; + document.querySelector('input[name=token]').value = ''; }); this.createElement().mountCardElement(); - document - .getElementById('pay-now') - .addEventListener('click', () => { - + document.getElementById('pay-now').addEventListener('click', () => { try { - let tokenInput = document.querySelector('input[name=token]'); + let tokenInput = + document.querySelector('input[name=token]'); if (tokenInput.value) { return this.completePaymentUsingToken(); } return this.completePaymentWithoutToken(); - }catch(error){ + } catch (error) { console.log(error.message); } - - }); + }); } } } -const publishableKey = - document.querySelector('meta[name="stripe-publishable-key"]')?.content ?? ''; +function boot() { + const publishableKey = + document.querySelector('meta[name="stripe-publishable-key"]') + ?.content ?? ''; -const secret = - document.querySelector('meta[name="stripe-secret"]')?.content ?? ''; + const secret = + document.querySelector('meta[name="stripe-secret"]')?.content ?? ''; -const onlyAuthorization = - document.querySelector('meta[name="only-authorization"]')?.content ?? ''; + const onlyAuthorization = + document.querySelector('meta[name="only-authorization"]')?.content ?? + ''; -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; -let s = new StripeCreditCard(publishableKey, secret, onlyAuthorization, stripeConnect); + let s = new StripeCreditCard( + publishableKey, + secret, + onlyAuthorization, + stripeConnect + ); -s.handle(); + s.handle(); +} -document.addEventListener('livewire:init', () => { - -Livewire.on('passed-required-fields-check', () => s.handle()); - -}); \ No newline at end of file +instant() ? boot() : wait('#stripe-credit-card-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-eps.js b/resources/js/clients/payments/stripe-eps.js index 2ae980bc321a..08e238f945ee 100644 --- a/resources/js/clients/payments/stripe-eps.js +++ b/resources/js/clients/payments/stripe-eps.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessEPSPay { constructor(key, stripeConnect) { this.key = key; @@ -18,13 +20,13 @@ class ProcessEPSPay { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -56,7 +58,7 @@ class ProcessEPSPay { if (!document.getElementById('eps-name').value) { errors.textContent = document.querySelector('meta[name=translation-name-required]').content; errors.hidden = false; - return ; + return; } document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); @@ -80,11 +82,15 @@ class ProcessEPSPay { }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessEPSPay(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; - -new ProcessEPSPay(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-eps-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-fpx.js b/resources/js/clients/payments/stripe-fpx.js index 45db81feea30..2a6b1cb76301 100644 --- a/resources/js/clients/payments/stripe-fpx.js +++ b/resources/js/clients/payments/stripe-fpx.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessFPXPay { constructor(key, stripeConnect) { this.key = key; @@ -18,13 +20,13 @@ class ProcessFPXPay { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -39,7 +41,7 @@ class ProcessFPXPay { fontSize: '16px', }, }; - this.fpx = elements.create('fpxBank', {style: style, accountHolderType: 'individual',}); + this.fpx = elements.create('fpxBank', { style: style, accountHolderType: 'individual', }); this.fpx.mount("#fpx-bank-element"); return this; }; @@ -81,13 +83,15 @@ class ProcessFPXPay { } } +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessFPXPay(publishableKey, stripeConnect).setupStripe().handle(); +} - -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; - -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; - -new ProcessFPXPay(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-fpx-payment').then(() => boot()); diff --git a/resources/js/clients/payments/stripe-giropay.js b/resources/js/clients/payments/stripe-giropay.js index 1a3903e9570f..7ab942de8376 100644 --- a/resources/js/clients/payments/stripe-giropay.js +++ b/resources/js/clients/payments/stripe-giropay.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessGiroPay { constructor(key, stripeConnect) { this.key = key; @@ -17,13 +19,13 @@ class ProcessGiroPay { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -41,7 +43,7 @@ class ProcessGiroPay { errors.textContent = document.querySelector('meta[name=translation-terms-required]').content; errors.hidden = false; console.log("Terms"); - return ; + return; } document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); @@ -64,11 +66,15 @@ class ProcessGiroPay { }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessGiroPay(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; - -new ProcessGiroPay(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-giropay-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/js/clients/payments/stripe-ideal.js b/resources/js/clients/payments/stripe-ideal.js index db397cb1e178..9831341370a2 100644 --- a/resources/js/clients/payments/stripe-ideal.js +++ b/resources/js/clients/payments/stripe-ideal.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessIDEALPay { constructor(key, stripeConnect) { this.key = key; @@ -18,13 +20,13 @@ class ProcessIDEALPay { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -57,7 +59,7 @@ class ProcessIDEALPay { errors.textContent = document.querySelector('meta[name=translation-name-required]').content; errors.hidden = false; console.log("name"); - return ; + return; } document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); @@ -81,11 +83,17 @@ class ProcessIDEALPay { }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessIDEALPay(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; +instant() ? boot() : wait('#stripe-ideal-payment').then(() => boot()); -new ProcessIDEALPay(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-ideal-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/js/clients/payments/stripe-klarna.js b/resources/js/clients/payments/stripe-klarna.js index cebbb6e42fcb..f81d95f080ac 100644 --- a/resources/js/clients/payments/stripe-klarna.js +++ b/resources/js/clients/payments/stripe-klarna.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessKlarna { constructor(key, stripeConnect) { this.key = key; @@ -17,7 +19,7 @@ class ProcessKlarna { setupStripe = () => { - if (this.stripeConnect){ + if (this.stripeConnect) { // this.stripe.stripeAccount = this.stripeConnect; this.stripe = Stripe(this.key, { @@ -47,7 +49,7 @@ class ProcessKlarna { document.getElementById('pay-now').addEventListener('click', (e) => { let errors = document.getElementById('errors'); let name = document.getElementById("klarna-name").value; - if (! /^[A-Za-z\s]*$/.test(name)){ + if (! /^[A-Za-z\s]*$/.test(name)) { document.getElementById('klarna-name-correction').hidden = false; document.getElementById('klarna-name').textContent = name.replace(/^[A-Za-z\s]*$/, "") document.getElementById('klarna-name').focus(); @@ -81,21 +83,28 @@ class ProcessKlarna { 'meta[name="return-url"]' ).content, } - ).then((result) => { + ).then((result) => { if (result.hasOwnProperty('error')) { return this.handleError(result.error.message); } - });} + }); + } }); }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessKlarna(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; +instant() ? boot() : wait('#stripe-klarna-payment').then(() => boot()); -new ProcessKlarna(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-klarna-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/js/clients/payments/stripe-przelewy24.js b/resources/js/clients/payments/stripe-przelewy24.js index 9f990240d0ec..6119ea7492fd 100644 --- a/resources/js/clients/payments/stripe-przelewy24.js +++ b/resources/js/clients/payments/stripe-przelewy24.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessPRZELEWY24 { constructor(key, stripeConnect) { this.key = key; @@ -17,19 +19,19 @@ class ProcessPRZELEWY24 { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); } - + let elements = this.stripe.elements() var options = { // Custom styling can be passed to options when creating an Element @@ -113,11 +115,17 @@ class ProcessPRZELEWY24 { }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessPRZELEWY24(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; +instant() ? boot() : wait('#stripe-przelewy24-payment').then(() => boot()); -new ProcessPRZELEWY24(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-przelewy24-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/js/clients/payments/stripe-sepa.js b/resources/js/clients/payments/stripe-sepa.js index cafcf8e7cc9c..d7f871c5843e 100644 --- a/resources/js/clients/payments/stripe-sepa.js +++ b/resources/js/clients/payments/stripe-sepa.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessSEPA { constructor(key, stripeConnect) { this.key = key; @@ -106,9 +108,9 @@ class ProcessSEPA { this.stripe .confirmSepaDebitPayment( document.querySelector('meta[name=pi-client-secret') - .content, { - payment_method: document.querySelector('input[name=token]').value - } + .content, { + payment_method: document.querySelector('input[name=token]').value + } ) .then((result) => { if (result.error) { @@ -147,28 +149,25 @@ class ProcessSEPA { return; } - document.getElementById('pay-now').disabled = true; document.querySelector('#pay-now > svg').classList.remove('hidden'); document.querySelector('#pay-now > span').classList.add('hidden'); - - this.stripe .confirmSepaDebitPayment( document.querySelector('meta[name=pi-client-secret') - .content, { - payment_method: { - sepa_debit: this.iban, - billing_details: { - name: document.getElementById('sepa-name') - .value, - email: document.getElementById( - 'sepa-email-address' - ).value, - }, + .content, { + payment_method: { + sepa_debit: this.iban, + billing_details: { + name: document.getElementById('sepa-name') + .value, + email: document.getElementById( + 'sepa-email-address' + ).value, }, - } + }, + } ) .then((result) => { if (result.error) { @@ -197,7 +196,7 @@ class ProcessSEPA { tokenBillingCheckbox.value; } - if(document.querySelector('input[name=token]').value.length > 2){ + if (document.querySelector('input[name=token]').value.length > 2) { document.querySelector('input[name="store_card"]').value = false; } @@ -233,11 +232,17 @@ class ProcessSEPA { } } -const publishableKey = - document.querySelector('meta[name="stripe-publishable-key"]')?.content ?? +function boot() { + const publishableKey = + document.querySelector('meta[name="stripe-publishable-key"]')?.content ?? ''; -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; -new ProcessSEPA(publishableKey, stripeConnect).setupStripe().handle(); + new ProcessSEPA(publishableKey, stripeConnect).setupStripe().handle(); +} + +instant() ? boot() : wait('#stripe-sepa-payment').then(() => boot()); + +instant() ? boot() : wait('#stripe-sepa-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/js/clients/payments/stripe-sofort.js b/resources/js/clients/payments/stripe-sofort.js index 394849697df4..c1bfd7f1e105 100644 --- a/resources/js/clients/payments/stripe-sofort.js +++ b/resources/js/clients/payments/stripe-sofort.js @@ -8,6 +8,8 @@ * @license https://www.elastic.co/licensing/elastic-license */ +import { wait, instant } from '../wait'; + class ProcessSOFORT { constructor(key, stripeConnect) { this.key = key; @@ -18,13 +20,13 @@ class ProcessSOFORT { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - + if (this.stripeConnect) { + // this.stripe.stripeAccount = this.stripeConnect; + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + } else { this.stripe = Stripe(this.key); @@ -58,11 +60,17 @@ class ProcessSOFORT { }; } -const publishableKey = document.querySelector( - 'meta[name="stripe-publishable-key"]' -)?.content ?? ''; +function boot() { + const publishableKey = document.querySelector( + 'meta[name="stripe-publishable-key"]' + )?.content ?? ''; + + const stripeConnect = + document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + + new ProcessSOFORT(publishableKey, stripeConnect).setupStripe().handle(); +} -const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; +instant() ? boot() : wait('#stripe-sofort-payment').then(() => boot()); -new ProcessSOFORT(publishableKey, stripeConnect).setupStripe().handle(); +instant() ? boot() : wait('#stripe-sofort-payment').then(() => boot()); \ No newline at end of file diff --git a/resources/views/email/admin/generic.blade.php b/resources/views/email/admin/generic.blade.php index 9177d9147da3..7c85385c7aa6 100644 --- a/resources/views/email/admin/generic.blade.php +++ b/resources/views/email/admin/generic.blade.php @@ -26,7 +26,7 @@

{{ $additional_info }}

@endisset - @isset($url) + @if($url) - @endisset + @endif @isset($signature)

{!! nl2br($signature) !!}

diff --git a/resources/views/email/client/generic.blade.php b/resources/views/email/client/generic.blade.php index 9f400d1049fb..7a36899ee98f 100644 --- a/resources/views/email/client/generic.blade.php +++ b/resources/views/email/client/generic.blade.php @@ -20,7 +20,7 @@

{{ $additional_info }}

@endisset - @isset($url) + @if($url)
- @endisset + @endif @isset($signature)

{!! nl2br($signature) !!}

diff --git a/resources/views/portal/ninja2020/flow2/error.blade.php b/resources/views/portal/ninja2020/flow2/error.blade.php new file mode 100644 index 000000000000..0d3496016a65 --- /dev/null +++ b/resources/views/portal/ninja2020/flow2/error.blade.php @@ -0,0 +1,11 @@ +
+ +

+ {{ ctrans('texts.error') }} +

+ +

+ {{ $errorMessage }} +

+ +
\ No newline at end of file diff --git a/resources/views/portal/ninja2020/flow2/invoice-pay.blade.php b/resources/views/portal/ninja2020/flow2/invoice-pay.blade.php index 728a295f2d5a..a2febb865db9 100644 --- a/resources/views/portal/ninja2020/flow2/invoice-pay.blade.php +++ b/resources/views/portal/ninja2020/flow2/invoice-pay.blade.php @@ -4,6 +4,15 @@
+ @if($errors->any()) +
+
    + @foreach($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif @livewire($this->component, [], key($this->componentUniqueId()))
diff --git a/resources/views/portal/ninja2020/flow2/invoices-summary.blade.php b/resources/views/portal/ninja2020/flow2/invoices-summary.blade.php index d5c9cde79a0a..e7dd811c0f78 100644 --- a/resources/views/portal/ninja2020/flow2/invoices-summary.blade.php +++ b/resources/views/portal/ninja2020/flow2/invoices-summary.blade.php @@ -16,30 +16,54 @@
@foreach($invoices as $invoice) -
-
{{ ctrans('texts.invoice') }} {{ $invoice->number }}
-
+
+
+
{{ ctrans('texts.invoice') }} {{ $invoice['number'] }}
+
{{ ctrans('texts.invoice_date') }}
-
{{ $invoice->translateDate($invoice->date, $invoice->client->date_format(), $invoice->client->locale()) }}
+
{{ $invoice['date'] }}
- @if($invoice->due_date) + @if($invoice['due_date'])
{{ ctrans('texts.due_date') }}
-
{{ $invoice->translateDate($invoice->due_date, $invoice->client->date_format(), $invoice->client->locale()) }}
+
{{ $invoice['due_date'] }}
@endif
{{ ctrans('texts.amount_due') }}
- {{ $invoice->client->currency()->code }} ({{ $invoice->client->currency()->symbol }}) - {{ $invoice->partial > 0 ? $invoice->partial : $invoice->balance }} + {{ $invoice['formatted_currency'] }}
+
+ +
@endforeach + +
{{ ctrans('texts.client_information') }}
diff --git a/resources/views/portal/ninja2020/flow2/payment-method.blade.php b/resources/views/portal/ninja2020/flow2/payment-method.blade.php index a1b8ecb2ebdd..72d17b1966ba 100644 --- a/resources/views/portal/ninja2020/flow2/payment-method.blade.php +++ b/resources/views/portal/ninja2020/flow2/payment-method.blade.php @@ -17,7 +17,7 @@ @foreach($methods as $index => $method) @endforeach @@ -62,4 +62,4 @@ }); @endscript -
\ No newline at end of file +
diff --git a/resources/views/portal/ninja2020/flow2/required-fields.blade.php b/resources/views/portal/ninja2020/flow2/required-fields.blade.php index 30b06e893555..5fd9576ca4b3 100644 --- a/resources/views/portal/ninja2020/flow2/required-fields.blade.php +++ b/resources/views/portal/ninja2020/flow2/required-fields.blade.php @@ -4,7 +4,7 @@

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

- + @if($is_loading) @@ -58,4 +58,4 @@
@endif - \ No newline at end of file + diff --git a/resources/views/portal/ninja2020/gateways/authorize/credit_card/authorize.blade.php b/resources/views/portal/ninja2020/gateways/authorize/credit_card/authorize.blade.php index 9cae6921c53d..1734e6696b01 100644 --- a/resources/views/portal/ninja2020/gateways/authorize/credit_card/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/authorize/credit_card/authorize.blade.php @@ -7,11 +7,9 @@ + - - - @endsection @section('gateway_content') @@ -51,25 +49,18 @@ @endif + + @vite('resources/js/clients/payment_methods/authorize-authorize-card.js') @endsection @push('footer') -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay.blade.php index cf5b09816890..b8fef635ce60 100644 --- a/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay.blade.php @@ -3,12 +3,10 @@ @section('gateway_head') + - - - @endsection @section('gateway_content') @@ -71,25 +69,17 @@ @endif + @vite('resources/js/clients/payments/authorize-credit-card-payment.js') @endsection @push('footer') -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay_livewire.blade.php b/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay_livewire.blade.php index d591a0561ef0..b708e3e85a88 100644 --- a/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay_livewire.blade.php +++ b/resources/views/portal/ninja2020/gateways/authorize/credit_card/pay_livewire.blade.php @@ -63,5 +63,6 @@ @endif + @vite('resources/js/clients/payments/authorize-credit-card-payment.js') @endassets diff --git a/resources/views/portal/ninja2020/gateways/authorize/includes/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/authorize/includes/credit_card.blade.php index db384defa35b..abb66b7a3157 100644 --- a/resources/views/portal/ninja2020/gateways/authorize/includes/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/authorize/includes/credit_card.blade.php @@ -1,11 +1,16 @@
-
- - - - - +
+ + +
+ + +
+ + +
diff --git a/resources/views/portal/ninja2020/gateways/braintree/ach/authorize.blade.php b/resources/views/portal/ninja2020/gateways/braintree/ach/authorize.blade.php index 602ece639370..a432d7bef067 100644 --- a/resources/views/portal/ninja2020/gateways/braintree/ach/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/braintree/ach/authorize.blade.php @@ -1,7 +1,8 @@ @extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH']) @section('gateway_head') - + + @endsection @section('gateway_content') @@ -19,7 +20,15 @@ - + + + @isset($payment_hash) + + @endisset + + @isset($authorize_then_redirect) + + @endisset @@ -93,6 +102,5 @@ - - + @vite('resources/js/clients/payment_methods/braintree-ach.js') @endsection diff --git a/resources/views/portal/ninja2020/gateways/braintree/ach/authorize_livewire.blade.php b/resources/views/portal/ninja2020/gateways/braintree/ach/authorize_livewire.blade.php new file mode 100644 index 000000000000..afd1d8fb27e4 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/braintree/ach/authorize_livewire.blade.php @@ -0,0 +1,102 @@ +
+ + + @if(session()->has('ach_error')) +
+

{{ session('ach_error') }}

+
+ @endif + +
+ @csrf + + + + + + + + @isset($payment_hash) + + @endif + + @isset($authorize_then_redirect) + + @endisset +
+ + + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_type')]) + + + {{ __('texts.checking') }} + + + + {{ __('texts.savings') }} + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_type')]) + + + {{ __('texts.individual_account') }} + + + + {{ __('texts.company_account') }} + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_name')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_number')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.routing_number')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.address1')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.address2')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.locality')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.state')]) + + @endcomponent + + @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.postal_code')]) + + @endcomponent + + @component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-bank-account']) + {{ ctrans('texts.add_payment_method') }} + @endcomponent +
+ +@assets + + + + @vite('resources/js/clients/payment_methods/braintree-ach.js') +@endassets \ No newline at end of file diff --git a/resources/views/portal/ninja2020/gateways/braintree/ach/pay.blade.php b/resources/views/portal/ninja2020/gateways/braintree/ach/pay.blade.php index c89e84297f30..ea582c9ff3d6 100644 --- a/resources/views/portal/ninja2020/gateways/braintree/ach/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/braintree/ach/pay.blade.php @@ -55,5 +55,14 @@ document.getElementById('server-response').submit(); }); + + /** + * @type {HTMLInputElement|null} + */ + const first = document.querySelector('input[name="payment-type"]'); + + if (first) { + first.click(); + } @endpush diff --git a/resources/views/portal/ninja2020/gateways/braintree/ach/pay_livewire.blade.php b/resources/views/portal/ninja2020/gateways/braintree/ach/pay_livewire.blade.php index 02ffa679e52c..928e93fb6055 100644 --- a/resources/views/portal/ninja2020/gateways/braintree/ach/pay_livewire.blade.php +++ b/resources/views/portal/ninja2020/gateways/braintree/ach/pay_livewire.blade.php @@ -54,5 +54,15 @@ document.getElementById('server-response').submit(); }); + + + /** + * @type {HTMLInputElement|null} + */ + const first = document.querySelector('input[name="payment-type"]'); + + if (first) { + first.click(); + } @endscript diff --git a/resources/views/portal/ninja2020/gateways/braintree/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/braintree/credit_card/pay.blade.php index e9f1c44f9b4c..771a5f3e5530 100644 --- a/resources/views/portal/ninja2020/gateways/braintree/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/braintree/credit_card/pay.blade.php @@ -2,6 +2,7 @@ @section('gateway_head') + {{-- --}} diff --git a/resources/views/portal/ninja2020/gateways/braintree/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/braintree/paypal/pay.blade.php index b405cebafed9..b1ee5eadb370 100644 --- a/resources/views/portal/ninja2020/gateways/braintree/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/braintree/paypal/pay.blade.php @@ -2,6 +2,7 @@ @section('gateway_head') + @@ -59,6 +60,11 @@ @component('portal.ninja2020.components.general.card-element-single')
+ + @endcomponent @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token', 'class' => 'hidden']) diff --git a/resources/views/portal/ninja2020/gateways/braintree/paypal/pay_livewire.blade.php b/resources/views/portal/ninja2020/gateways/braintree/paypal/pay_livewire.blade.php index ac4ac9690c31..a1d4962918bc 100644 --- a/resources/views/portal/ninja2020/gateways/braintree/paypal/pay_livewire.blade.php +++ b/resources/views/portal/ninja2020/gateways/braintree/paypal/pay_livewire.blade.php @@ -53,6 +53,11 @@ @component('portal.ninja2020.components.general.card-element-single')
+ + @endcomponent @include('portal.ninja2020.gateways.includes.pay_now', ['id' => 'pay-now-with-token', 'class' => 'hidden']) diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php index 308cba1a0656..bbcfed3655cd 100644 --- a/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card/pay.blade.php @@ -6,6 +6,7 @@ + @include('portal.ninja2020.gateways.checkout.credit_card.includes.styles') diff --git a/resources/views/portal/ninja2020/gateways/eway/authorize.blade.php b/resources/views/portal/ninja2020/gateways/eway/authorize.blade.php index 7f062ea3ca63..e33106a9f9cc 100644 --- a/resources/views/portal/ninja2020/gateways/eway/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/eway/authorize.blade.php @@ -7,6 +7,7 @@ ctrans('texts.credit_card')]) + @endsection @section('gateway_content') diff --git a/resources/views/portal/ninja2020/gateways/eway/pay.blade.php b/resources/views/portal/ninja2020/gateways/eway/pay.blade.php index 3f0cc9c788a1..98c7805b886c 100644 --- a/resources/views/portal/ninja2020/gateways/eway/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/eway/pay.blade.php @@ -7,6 +7,7 @@ ctrans('texts.credit_card')]) + @endsection @section('gateway_content') diff --git a/resources/views/portal/ninja2020/gateways/forte/ach/pay.blade.php b/resources/views/portal/ninja2020/gateways/forte/ach/pay.blade.php index 8dfab33c4269..89d1a308f20a 100644 --- a/resources/views/portal/ninja2020/gateways/forte/ach/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/forte/ach/pay.blade.php @@ -2,6 +2,7 @@ @section('gateway_head') + @endsection @section('gateway_content') diff --git a/resources/views/portal/ninja2020/gateways/forte/credit_card/authorize.blade.php b/resources/views/portal/ninja2020/gateways/forte/credit_card/authorize.blade.php index 577999262a05..3bf591d8136d 100644 --- a/resources/views/portal/ninja2020/gateways/forte/credit_card/authorize.blade.php +++ b/resources/views/portal/ninja2020/gateways/forte/credit_card/authorize.blade.php @@ -6,9 +6,8 @@ - + - @if($gateway->company_gateway->getConfigField('testMode')) @else @@ -70,7 +69,19 @@ @endsection @section('gateway_footer') - diff --git a/resources/views/portal/ninja2020/gateways/forte/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/forte/credit_card/pay.blade.php index bb5901a58230..bfc291f2415f 100644 --- a/resources/views/portal/ninja2020/gateways/forte/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/forte/credit_card/pay.blade.php @@ -2,10 +2,9 @@ @section('gateway_head') - - + - + @endsection @section('gateway_content') @@ -78,26 +77,19 @@ @else @endif + + @vite('resources/js/clients/payments/forte-credit-card-payment.js') @endsection @push('footer') -@endpush \ No newline at end of file +@endpush diff --git a/resources/views/portal/ninja2020/gateways/forte/credit_card/pay_livewire.blade.php b/resources/views/portal/ninja2020/gateways/forte/credit_card/pay_livewire.blade.php index cff7d20ca4a2..63056fb20178 100644 --- a/resources/views/portal/ninja2020/gateways/forte/credit_card/pay_livewire.blade.php +++ b/resources/views/portal/ninja2020/gateways/forte/credit_card/pay_livewire.blade.php @@ -33,11 +33,14 @@
@assets + @if($gateway->company_gateway->getConfigField('testMode')) @else @endif + + @vite('resources/js/clients/payments/forte-credit-card-payment.js') @endassets diff --git a/resources/views/portal/ninja2020/gateways/forte/includes/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/forte/includes/credit_card.blade.php index b15c3332764a..225c767e4da8 100644 --- a/resources/views/portal/ninja2020/gateways/forte/includes/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/forte/includes/credit_card.blade.php @@ -1,11 +1,16 @@
- - - - - + + +
+ + +
+ + +
diff --git a/resources/views/portal/ninja2020/gateways/gocardless/ach/pay.blade.php b/resources/views/portal/ninja2020/gateways/gocardless/ach/pay.blade.php index aeb535ef3755..37c83c7f6c17 100644 --- a/resources/views/portal/ninja2020/gateways/gocardless/ach/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/gocardless/ach/pay.blade.php @@ -53,5 +53,13 @@ document.getElementById('pay-now').addEventListener('click', function() { document.getElementById('server-response').submit(); }); + + + const first = document.querySelector('input[name="payment-type"]'); + + if (first) { + first.click(); + } + @endpush diff --git a/resources/views/portal/ninja2020/gateways/gocardless/ach/pay_livewire.blade.php b/resources/views/portal/ninja2020/gateways/gocardless/ach/pay_livewire.blade.php index 44253a35ab5f..e67984392be7 100644 --- a/resources/views/portal/ninja2020/gateways/gocardless/ach/pay_livewire.blade.php +++ b/resources/views/portal/ninja2020/gateways/gocardless/ach/pay_livewire.blade.php @@ -51,6 +51,13 @@ document.getElementById('pay-now').addEventListener('click', function() { document.getElementById('server-response').submit(); }); + + const first = document.querySelector('input[name="payment-type"]'); + + if (first) { + first.click(); + } + @endscript diff --git a/resources/views/portal/ninja2020/gateways/gocardless/direct_debit/pay.blade.php b/resources/views/portal/ninja2020/gateways/gocardless/direct_debit/pay.blade.php index 75ff0ee4f077..91db7ea32020 100644 --- a/resources/views/portal/ninja2020/gateways/gocardless/direct_debit/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/gocardless/direct_debit/pay.blade.php @@ -19,7 +19,7 @@ @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')]) @if (count($tokens) > 0) @foreach ($tokens as $token) -