mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 20:52:56 +01:00
Merge pull request #16 from M-E-Development-Design/v5-develop
V5 develop
This commit is contained in:
commit
7d9bc7adf2
6
.github/workflows/react_release.yml
vendored
6
.github/workflows/react_release.yml
vendored
@ -38,6 +38,9 @@ jobs:
|
||||
sudo php artisan cache:clear
|
||||
sudo find ./vendor/bin/ -type f -exec chmod +x {} \;
|
||||
sudo find ./ -type d -exec chmod 755 {} \;
|
||||
- name: Set current date to variable
|
||||
id: set_date
|
||||
run: echo "current_date=$(date '+%Y-%m-%d')" >> $GITHUB_ENV
|
||||
|
||||
- name: Prepare React FrontEnd
|
||||
run: |
|
||||
@ -46,10 +49,11 @@ jobs:
|
||||
git checkout develop
|
||||
cp .env.example .env
|
||||
cp ../vite.config.ts.react ./vite.config.js
|
||||
sed -i '/"version"/c\ "version": " Latest Build - ${{ env.current_date }}",' package.json
|
||||
npm i
|
||||
npm run build
|
||||
cp -r dist/* ../public/
|
||||
mv dist/index.html ../resources/views/react/index.blade.php
|
||||
mv ../public/index.html ../resources/views/react/index.blade.php
|
||||
|
||||
- name: Prepare JS/CSS assets
|
||||
run: |
|
||||
|
@ -1 +1 @@
|
||||
5.10.13
|
||||
5.10.16
|
@ -177,7 +177,6 @@ class BackupUpdate extends Command
|
||||
$doc_bin = $document->getFile();
|
||||
} catch(\Exception $e) {
|
||||
nlog("Exception:: BackupUpdate::" . $e->getMessage());
|
||||
nlog($e->getMessage());
|
||||
}
|
||||
|
||||
if ($doc_bin) {
|
||||
|
@ -172,6 +172,7 @@ class BaseExport
|
||||
'tax_rate3' => 'invoice.tax_rate3',
|
||||
'recurring_invoice' => 'invoice.recurring_id',
|
||||
'auto_bill' => 'invoice.auto_bill_enabled',
|
||||
'project' => 'invoice.project',
|
||||
];
|
||||
|
||||
protected array $recurring_invoice_report_keys = [
|
||||
@ -1038,6 +1039,10 @@ class BaseExport
|
||||
|
||||
$recurring_filters = [];
|
||||
|
||||
if($this->company->getSetting('report_include_drafts')){
|
||||
$recurring_filters[] = RecurringInvoice::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
if (in_array('active', $status_parameters)) {
|
||||
$recurring_filters[] = RecurringInvoice::STATUS_ACTIVE;
|
||||
}
|
||||
|
@ -153,9 +153,9 @@ class InvoiceExport extends BaseExport
|
||||
private function decorateAdvancedFields(Invoice $invoice, array $entity): array
|
||||
{
|
||||
|
||||
// if (in_array('invoice.status', $this->input['report_keys'])) {
|
||||
// $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
|
||||
// }
|
||||
if (in_array('invoice.project', $this->input['report_keys'])) {
|
||||
$entity['invoice.project'] = $invoice->project ? $invoice->project->name : '';
|
||||
}
|
||||
|
||||
if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
|
||||
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
|
||||
|
@ -265,6 +265,10 @@ class InvoiceItemExport extends BaseExport
|
||||
$entity['invoice.user_id'] = $invoice->user ? $invoice->user->present()->name() : '';// @phpstan-ignore-line
|
||||
}
|
||||
|
||||
if (in_array('invoice.project', $this->input['report_keys'])) {
|
||||
$entity['invoice.project'] = $invoice->project ? $invoice->project->name : '';// @phpstan-ignore-line
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ class InvoiceDecorator extends Decorator implements DecoratorInterface
|
||||
{
|
||||
return $invoice->recurring_invoice ? $invoice->recurring_invoice->number : '';
|
||||
}
|
||||
|
||||
public function auto_bill_enabled(Invoice $invoice)
|
||||
{
|
||||
return $invoice->auto_bill_enabled ? ctrans('texts.yes') : ctrans('texts.no');
|
||||
|
@ -153,22 +153,22 @@ class InvoiceFilters extends QueryFilters
|
||||
{
|
||||
|
||||
return $this->builder->where(function ($query) {
|
||||
$query->whereIn('invoices.status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
|
||||
->where('invoices.is_deleted', 0)
|
||||
->where('invoices.balance', '>', 0)
|
||||
->orWhere(function ($query) {
|
||||
$query->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
|
||||
->where('is_deleted', 0)
|
||||
->where('balance', '>', 0)
|
||||
->where(function ($query) {
|
||||
|
||||
$query->whereNull('invoices.due_date')
|
||||
$query->whereNull('due_date')
|
||||
->orWhere(function ($q) {
|
||||
$q->where('invoices.due_date', '>=', now()->startOfDay()->subSecond())->where('invoices.partial', 0);
|
||||
$q->where('due_date', '>=', now()->startOfDay()->subSecond())->where('partial', 0);
|
||||
})
|
||||
->orWhere(function ($q) {
|
||||
$q->where('invoices.partial_due_date', '>=', now()->startOfDay()->subSecond())->where('invoices.partial', '>', 0);
|
||||
$q->where('partial_due_date', '>=', now()->startOfDay()->subSecond())->where('partial', '>', 0);
|
||||
});
|
||||
|
||||
})
|
||||
->orderByRaw('ISNULL(invoices.due_date), invoices.due_date ' . 'desc')
|
||||
->orderByRaw('ISNULL(invoices.partial_due_date), invoices.partial_due_date ' . 'desc');
|
||||
->orderByRaw('ISNULL(due_date), due_date ' . 'desc')
|
||||
->orderByRaw('ISNULL(partial_due_date), partial_due_date ' . 'desc');
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class ChartController extends BaseController
|
||||
return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200);
|
||||
}
|
||||
|
||||
public function calculatedField(ShowCalculatedFieldRequest $request)
|
||||
public function calculatedFields(ShowCalculatedFieldRequest $request)
|
||||
{
|
||||
|
||||
/** @var \App\Models\User auth()->user() */
|
||||
|
@ -300,7 +300,9 @@ class InvitationController extends Controller
|
||||
'signature' => false,
|
||||
'contact_first_name' => $invitation->contact->first_name ?? '',
|
||||
'contact_last_name' => $invitation->contact->last_name ?? '',
|
||||
'contact_email' => $invitation->contact->email ?? ''
|
||||
'contact_email' => $invitation->contact->email ?? '',
|
||||
'client_city' => $invitation->client->city ?? '',
|
||||
'client_postal_code' => $invitation->client->postal_code ?? '',
|
||||
];
|
||||
|
||||
$request->replace($data);
|
||||
|
@ -108,11 +108,11 @@ class PaymentController extends Controller
|
||||
*/
|
||||
public function process(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'contact_first_name' => ['required'],
|
||||
'contact_last_name' => ['required'],
|
||||
'contact_email' => ['required', 'email'],
|
||||
]);
|
||||
// $request->validate([
|
||||
// 'contact_first_name' => ['required'],
|
||||
// 'contact_last_name' => ['required'],
|
||||
// 'contact_email' => ['required', 'email'],
|
||||
// ]);
|
||||
|
||||
return (new InstantPayment($request))->run();
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class ImportController extends Controller
|
||||
$contents = $this->convertEncoding($contents);
|
||||
|
||||
// Store the csv in cache with an expiry of 10 minutes
|
||||
Cache::put($hash.'-'.$entityType, base64_encode($contents), 600);
|
||||
Cache::put($hash.'-'.$entityType, base64_encode($contents), 1200);
|
||||
|
||||
// Parse CSV
|
||||
$csv_array = $this->getCsvData($contents);
|
||||
|
@ -19,8 +19,9 @@ use Illuminate\Contracts\Validation\ValidationRule;
|
||||
*/
|
||||
class BlackListRule implements ValidationRule
|
||||
{
|
||||
/** Bad domains +/- dispoable email domains */
|
||||
/** Bad domains +/- disposable email domains */
|
||||
private array $blacklist = [
|
||||
'padvn.com',
|
||||
'anonaddy.me',
|
||||
'nqmo.com',
|
||||
'wireconnected.com',
|
||||
|
@ -98,7 +98,7 @@ class BaseImport
|
||||
}
|
||||
|
||||
/** @var string $base64_encoded_csv */
|
||||
$base64_encoded_csv = Cache::pull($this->hash.'-'.$entity_type);
|
||||
$base64_encoded_csv = Cache::get($this->hash.'-'.$entity_type);
|
||||
|
||||
if (empty($base64_encoded_csv)) {
|
||||
return null;
|
||||
@ -473,6 +473,8 @@ class BaseImport
|
||||
|
||||
$tasks = $this->groupTasks($tasks, $task_number_key);
|
||||
|
||||
nlog($tasks);
|
||||
|
||||
foreach ($tasks as $raw_task) {
|
||||
$task_data = [];
|
||||
|
||||
@ -702,16 +704,16 @@ class BaseImport
|
||||
->save();
|
||||
}
|
||||
|
||||
if ($invoice->status_id === Invoice::STATUS_DRAFT) {
|
||||
} elseif ($invoice->status_id === Invoice::STATUS_SENT) {
|
||||
$invoice = $invoice
|
||||
->service()
|
||||
->markSent()
|
||||
->save();
|
||||
} elseif (
|
||||
$invoice->status_id <= Invoice::STATUS_SENT &&
|
||||
$invoice->amount > 0
|
||||
) {
|
||||
if ($invoice->status_id == Invoice::STATUS_DRAFT) {
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
$invoice = $invoice
|
||||
->service()
|
||||
->markSent()
|
||||
->save();
|
||||
|
||||
if ($invoice->status_id <= Invoice::STATUS_SENT && $invoice->amount > 0) {
|
||||
if ($invoice->balance <= 0) {
|
||||
$invoice->status_id = Invoice::STATUS_PAID;
|
||||
$invoice->save();
|
||||
|
@ -172,7 +172,7 @@ class Wave extends BaseImport implements ImportInterface
|
||||
{
|
||||
$entity_type = 'expense';
|
||||
|
||||
$data = $this->getCsvData($entity_type);
|
||||
$data = $this->getCsvData('invoice');
|
||||
|
||||
if (!$data) {
|
||||
$this->entity_count['expense'] = 0;
|
||||
@ -244,14 +244,17 @@ class Wave extends BaseImport implements ImportInterface
|
||||
if (empty($expense_data['vendor_id'])) {
|
||||
$vendor_data['user_id'] = $this->getUserIDForRecord($expense_data);
|
||||
|
||||
$vendor_repository->save(
|
||||
['name' => $raw_expense['Vendor Name']],
|
||||
$vendor = VendorFactory::create(
|
||||
$this->company->id,
|
||||
$vendor_data['user_id']
|
||||
)
|
||||
);
|
||||
$expense_data['vendor_id'] = $vendor->id;
|
||||
if(isset($raw_expense['Vendor Name']) || isset($raw_expense['Vendor']))
|
||||
{
|
||||
$vendor_repository->save(
|
||||
['name' => isset($raw_expense['Vendor Name']) ? $raw_expense['Vendor Name'] : isset($raw_expense['Vendor'])],
|
||||
$vendor = VendorFactory::create(
|
||||
$this->company->id,
|
||||
$vendor_data['user_id']
|
||||
)
|
||||
);
|
||||
$expense_data['vendor_id'] = $vendor->id;
|
||||
}
|
||||
}
|
||||
|
||||
$validator = Validator::make(
|
||||
|
@ -46,6 +46,7 @@ class TaskTransformer extends BaseTransformer
|
||||
'company_id' => $this->company->id,
|
||||
'number' => $this->getString($task_data, 'task.number'),
|
||||
'user_id' => $this->getString($task_data, 'task.user_id'),
|
||||
'rate' => $this->getFloat($task_data, 'task.rate'),
|
||||
'client_id' => $clientId,
|
||||
'project_id' => $this->getProjectId($projectId, $clientId),
|
||||
'description' => $this->getString($task_data, 'task.description'),
|
||||
@ -87,8 +88,7 @@ class TaskTransformer extends BaseTransformer
|
||||
$is_billable = true;
|
||||
}
|
||||
|
||||
if(isset($item['task.start_date']) &&
|
||||
isset($item['task.end_date'])) {
|
||||
if(isset($item['task.start_date'])) {
|
||||
$start_date = $this->resolveStartDate($item);
|
||||
$end_date = $this->resolveEndDate($item);
|
||||
} elseif(isset($item['task.duration'])) {
|
||||
@ -136,7 +136,7 @@ class TaskTransformer extends BaseTransformer
|
||||
private function resolveEndDate($item)
|
||||
{
|
||||
|
||||
$stub_end_date = $item['task.end_date'];
|
||||
$stub_end_date = isset($item['task.end_date']) ? $item['task.end_date'] : $item['task.start_date'];
|
||||
$stub_end_date .= isset($item['task.end_time']) ? " ".$item['task.end_time'] : '';
|
||||
|
||||
try {
|
||||
|
@ -36,18 +36,26 @@ class ExpenseTransformer extends BaseTransformer
|
||||
$total_tax += floatval($record['Sales Tax Amount']);
|
||||
}
|
||||
|
||||
$tax_rate = round(($total_tax / $amount) * 100, 3);
|
||||
$tax_rate = $total_tax > 0 ? round(($total_tax / $amount) * 100, 3) : 0;
|
||||
|
||||
if(isset($data['Notes / Memo']) && strlen($data['Notes / Memo']) > 1)
|
||||
$public_notes = $data['Notes / Memo'];
|
||||
elseif (isset($data['Transaction Description']) && strlen($data['Transaction Description']) > 1)
|
||||
$public_notes = $data['Transaction Description'];
|
||||
else
|
||||
$public_notes = '';
|
||||
|
||||
|
||||
$transformed = [
|
||||
'company_id' => $this->company->id,
|
||||
'vendor_id' => $this->getVendorIdOrCreate($this->getString($data, 'Vendor')),
|
||||
'number' => $this->getString($data, 'Bill Number'),
|
||||
'public_notes' => $this->getString($data, 'Notes / Memo'),
|
||||
'public_notes' => $public_notes,
|
||||
'date' => $this->parseDate($data['Transaction Date Added']) ?: now()->format('Y-m-d'), //27-01-2022
|
||||
'currency_id' => $this->company->settings->currency_id,
|
||||
'category_id' => $this->getOrCreateExpenseCategry($data['Account Name']),
|
||||
'amount' => $amount,
|
||||
'tax_name1' => $data['Sales Tax Name'],
|
||||
'tax_name1' => isset($data['Sales Tax Name']) ? $data['Sales Tax Name'] : '',
|
||||
'tax_rate1' => $tax_rate,
|
||||
];
|
||||
|
||||
|
@ -188,6 +188,10 @@ class BillingPortalPurchase extends Component
|
||||
|
||||
public ?string $contact_email;
|
||||
|
||||
public ?string $client_city;
|
||||
|
||||
public ?string $client_postal_code;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->db);
|
||||
@ -203,7 +207,7 @@ class BillingPortalPurchase extends Component
|
||||
if (request()->query('coupon')) {
|
||||
$this->coupon = request()->query('coupon');
|
||||
$this->handleCoupon();
|
||||
} elseif (strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0) {
|
||||
} elseif (strlen($this->subscription->promo_code ?? '') == 0 && $this->subscription->promo_discount > 0) {
|
||||
$this->price = $this->subscription->promo_price;
|
||||
}
|
||||
|
||||
@ -335,10 +339,6 @@ class BillingPortalPurchase extends Component
|
||||
{
|
||||
$this->contact = $contact;
|
||||
|
||||
if ($contact->showRff()) {
|
||||
return $this->rff();
|
||||
}
|
||||
|
||||
Auth::guard('contact')->loginUsingId($contact->id, true);
|
||||
|
||||
if ($this->subscription->trial_enabled) {
|
||||
@ -351,11 +351,20 @@ class BillingPortalPurchase extends Component
|
||||
if ((int)$this->price == 0) {
|
||||
$this->steps['payment_required'] = false;
|
||||
} else {
|
||||
$this->steps['fetched_payment_methods'] = true;
|
||||
// $this->steps['fetched_payment_methods'] = true;
|
||||
}
|
||||
|
||||
$this->methods = $contact->client->service()->getPaymentMethods($this->price);
|
||||
|
||||
foreach($this->methods as $method){
|
||||
|
||||
if($method['is_paypal'] == '1' && !$this->steps['check_rff']){
|
||||
$this->rff();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->heading_text = ctrans('texts.payment_methods');
|
||||
|
||||
return $this;
|
||||
@ -366,6 +375,8 @@ class BillingPortalPurchase extends Component
|
||||
$this->contact_first_name = $this->contact->first_name;
|
||||
$this->contact_last_name = $this->contact->last_name;
|
||||
$this->contact_email = $this->contact->email;
|
||||
$this->client_city = $this->contact->client->city;
|
||||
$this->client_postal_code = $this->contact->client->postal_code;
|
||||
|
||||
$this->steps['check_rff'] = true;
|
||||
|
||||
@ -377,13 +388,20 @@ class BillingPortalPurchase extends Component
|
||||
$validated = $this->validate([
|
||||
'contact_first_name' => ['required'],
|
||||
'contact_last_name' => ['required'],
|
||||
'client_city' => ['required'],
|
||||
'client_postal_code' => ['required'],
|
||||
'contact_email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
$this->contact->first_name = $validated['contact_first_name'];
|
||||
$this->contact->last_name = $validated['contact_last_name'];
|
||||
$this->contact->email = $validated['contact_email'];
|
||||
$this->contact->save();
|
||||
$this->contact->client->postal_code = $validated['client_postal_code'];
|
||||
$this->contact->client->city = $validated['client_city'];
|
||||
|
||||
$this->contact->pushQuietly();
|
||||
|
||||
$this->steps['fetched_payment_methods'] = true;
|
||||
|
||||
return $this->getPaymentMethods($this->contact);
|
||||
}
|
||||
@ -395,13 +413,13 @@ class BillingPortalPurchase extends Component
|
||||
* @param $company_gateway_id
|
||||
* @param $gateway_type_id
|
||||
*/
|
||||
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
|
||||
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id, $is_paypal = false)
|
||||
{
|
||||
$this->company_gateway_id = $company_gateway_id;
|
||||
$this->payment_method_id = $gateway_type_id;
|
||||
|
||||
$this->handleBeforePaymentEvents();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,6 +164,13 @@ class BillingPortalPurchasev2 extends Component
|
||||
public $payment_confirmed = false;
|
||||
public $is_eligible = true;
|
||||
public $not_eligible_message = '';
|
||||
public $check_rff = false;
|
||||
|
||||
public ?string $contact_first_name;
|
||||
public ?string $contact_last_name;
|
||||
public ?string $contact_email;
|
||||
public ?string $client_city;
|
||||
public ?string $client_postal_code;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
@ -472,7 +479,6 @@ class BillingPortalPurchasev2 extends Component
|
||||
*/
|
||||
protected function getPaymentMethods(): self
|
||||
{
|
||||
nlog("total amount = {$this->float_amount_total}");
|
||||
|
||||
if ($this->float_amount_total == 0) {
|
||||
$this->methods = [];
|
||||
@ -481,10 +487,73 @@ class BillingPortalPurchasev2 extends Component
|
||||
if ($this->contact && $this->float_amount_total >= 1) {
|
||||
$this->methods = $this->contact->client->service()->getPaymentMethods($this->float_amount_total);
|
||||
}
|
||||
|
||||
foreach($this->methods as $method) {
|
||||
|
||||
if($method['is_paypal'] == '1' && !$this->check_rff) {
|
||||
$this->rff();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function rff()
|
||||
{
|
||||
|
||||
$this->contact_first_name = $this->contact->first_name;
|
||||
$this->contact_last_name = $this->contact->last_name;
|
||||
$this->contact_email = $this->contact->email;
|
||||
$this->client_city = $this->contact->client->city;
|
||||
$this->client_postal_code = $this->contact->client->postal_code;
|
||||
|
||||
if(
|
||||
strlen($this->contact_first_name ?? '') == 0 ||
|
||||
strlen($this->contact_last_name ?? '') == 0 ||
|
||||
strlen($this->contact_email ?? '') == 0 ||
|
||||
strlen($this->client_city ?? '') == 0 ||
|
||||
strlen($this->client_postal_code ?? '') == 0
|
||||
)
|
||||
{
|
||||
$this->check_rff = true;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleRff()
|
||||
{
|
||||
|
||||
$validated = $this->validate([
|
||||
'contact_first_name' => ['required'],
|
||||
'contact_last_name' => ['required'],
|
||||
'client_city' => ['required'],
|
||||
'client_postal_code' => ['required'],
|
||||
'contact_email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
$this->check_rff = false;
|
||||
|
||||
$this->contact->first_name = $validated['contact_first_name'];
|
||||
$this->contact->last_name = $validated['contact_last_name'];
|
||||
$this->contact->email = $validated['contact_email'];
|
||||
$this->contact->client->postal_code = $validated['client_postal_code'];
|
||||
$this->contact->client->city = $validated['client_city'];
|
||||
|
||||
$this->contact->pushQuietly();
|
||||
|
||||
$this->refreshComponent();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function refreshComponent()
|
||||
{
|
||||
$this->dispatch('$refresh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Middle method between selecting payment method &
|
||||
* submitting the from to the backend.
|
||||
|
@ -351,9 +351,9 @@ class ClientContact extends Authenticatable implements HasLocalePreference
|
||||
|
||||
public function showRff(): bool
|
||||
{
|
||||
if (\strlen($this->first_name) === 0 || \strlen($this->last_name) === 0 || \strlen($this->email) === 0) {
|
||||
return true;
|
||||
}
|
||||
// if (\strlen($this->first_name ?? '') === 0 || \strlen($this->last_name ?? '') === 0 || \strlen($this->email ?? '') === 0) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -159,6 +159,11 @@ class CompanyGateway extends BaseModel
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
public function isPayPal()
|
||||
{
|
||||
return in_array($this->gateway_key, ['80af24a6a691230bbec33e930ab40666','80af24a6a691230bbec33e930ab40665']);
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
@ -141,23 +141,23 @@ class Gateway extends StaticModel
|
||||
case 20:
|
||||
case 56:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'customer.source.updated', 'payment_intent.processing', 'payment_intent.payment_failed', 'charge.failed']],
|
||||
GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => false, 'webhooks' => ['payment_intent.processing', 'payment_intent.succeeded', 'payment_intent.partially_funded', 'payment_intent.payment_failed']],
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded', 'charge.refunded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.refunded','charge.succeeded', 'customer.source.updated', 'payment_intent.processing', 'payment_intent.payment_failed', 'charge.failed']],
|
||||
GatewayType::DIRECT_DEBIT => ['refund' => false, 'token_billing' => false, 'webhooks' => ['payment_intent.processing', 'charge.refunded', 'payment_intent.succeeded', 'payment_intent.partially_funded', 'payment_intent.payment_failed']],
|
||||
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.processing', 'payment_intent.succeeded', 'mandate.updated', 'payment_intent.payment_failed']],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.failed',]],
|
||||
GatewayType::BACS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.processing', 'payment_intent.succeeded', 'mandate.updated', 'payment_intent.payment_failed']],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::EPS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed', 'payment_intent.succeeded', 'payment_intent.payment_failed']],
|
||||
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'charge.refunded', 'charge.failed',]],
|
||||
];
|
||||
case 39:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
|
||||
|
@ -129,7 +129,7 @@ class Project extends BaseModel
|
||||
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
return $this->hasMany(Invoice::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function quotes(): HasMany
|
||||
|
@ -170,6 +170,9 @@ class ACH
|
||||
];
|
||||
|
||||
$payment = $this->forte->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
return redirect('client/invoices')->withSuccess('Invoice paid.');
|
||||
// return redirect('client/invoices')->withSuccess('Invoice paid.');
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,8 @@ class CreditCard
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
];
|
||||
$payment = $this->forte->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
return redirect('client/invoices')->withSuccess('Invoice paid.');
|
||||
// return redirect('client/invoices')->withSuccess('Invoice paid.');
|
||||
return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -251,11 +251,11 @@ class PayPalBasePaymentDriver extends BaseDriver
|
||||
[
|
||||
"address" =>
|
||||
[
|
||||
"address_line_1" => strlen($this->client->shipping_address1) > 1 ? $this->client->shipping_address1 : $this->client->address1,
|
||||
"address_line_1" => strlen($this->client->shipping_address1 ?? '') > 1 ? $this->client->shipping_address1 : $this->client->address1,
|
||||
"address_line_2" => $this->client->shipping_address2,
|
||||
"admin_area_2" => strlen($this->client->shipping_city) > 1 ? $this->client->shipping_city : $this->client->city,
|
||||
"admin_area_1" => strlen($this->client->shipping_state) > 1 ? $this->client->shipping_state : $this->client->state,
|
||||
"postal_code" => strlen($this->client->shipping_postal_code) > 1 ? $this->client->shipping_postal_code : $this->client->postal_code,
|
||||
"admin_area_2" => strlen($this->client->shipping_city ?? '') > 1 ? $this->client->shipping_city : $this->client->city,
|
||||
"admin_area_1" => strlen($this->client->shipping_state ?? '') > 1 ? $this->client->shipping_state : $this->client->state,
|
||||
"postal_code" => strlen($this->client->shipping_postal_code ?? '') > 1 ? $this->client->shipping_postal_code : $this->client->postal_code,
|
||||
"country_code" => $this->client->present()->shipping_country_code(),
|
||||
],
|
||||
]
|
||||
|
@ -11,18 +11,22 @@
|
||||
|
||||
namespace App\PaymentDrivers\Stripe\Jobs;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\Payment;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\PaymentHash;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use App\Services\Email\Email;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Services\Email\EmailObject;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChargeRefunded implements ShouldQueue
|
||||
{
|
||||
@ -36,19 +40,10 @@ class ChargeRefunded implements ShouldQueue
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public $stripe_request;
|
||||
|
||||
public $company_key;
|
||||
|
||||
private $company_gateway_id;
|
||||
|
||||
public $payment_completed = false;
|
||||
|
||||
public function __construct($stripe_request, $company_key, $company_gateway_id)
|
||||
public function __construct(public array $stripe_request, private string $company_key)
|
||||
{
|
||||
$this->stripe_request = $stripe_request;
|
||||
$this->company_key = $company_key;
|
||||
$this->company_gateway_id = $company_gateway_id;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
@ -64,8 +59,8 @@ class ChargeRefunded implements ShouldQueue
|
||||
|
||||
$payment_hash_key = $source['metadata']['payment_hash'] ?? null;
|
||||
|
||||
$company_gateway = CompanyGateway::query()->find($this->company_gateway_id);
|
||||
$payment_hash = PaymentHash::query()->where('hash', $payment_hash_key)->first();
|
||||
$company_gateway = $payment_hash->payment->company_gateway;
|
||||
|
||||
$stripe_driver = $company_gateway->driver()->init();
|
||||
|
||||
@ -79,7 +74,7 @@ class ChargeRefunded implements ShouldQueue
|
||||
->first();
|
||||
|
||||
//don't touch if already refunded
|
||||
if(!$payment || in_array($payment->status_id, [Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])) {
|
||||
if(!$payment || $payment->status_id == Payment::STATUS_REFUNDED || $payment->is_deleted){
|
||||
return;
|
||||
}
|
||||
|
||||
@ -94,8 +89,19 @@ class ChargeRefunded implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
if($payment->status_id == Payment::STATUS_COMPLETED) {
|
||||
usleep(rand(200000,300000));
|
||||
$payment = $payment->fresh();
|
||||
|
||||
if($payment->status_id == Payment::STATUS_PARTIALLY_REFUNDED){
|
||||
//determine the delta in the refunded amount - how much has already been refunded and only apply the delta.
|
||||
|
||||
if(floatval($payment->refunded) >= floatval($amount_refunded))
|
||||
return;
|
||||
|
||||
$amount_refunded -= $payment->refunded;
|
||||
|
||||
}
|
||||
|
||||
$invoice_collection = $payment->paymentables
|
||||
->where('paymentable_type', 'invoices')
|
||||
->map(function ($pivot) {
|
||||
@ -117,9 +123,24 @@ class ChargeRefunded implements ShouldQueue
|
||||
];
|
||||
});
|
||||
|
||||
} elseif($invoice_collection->sum('amount') != $amount_refunded) {
|
||||
//too many edges cases at this point, return early
|
||||
}
|
||||
elseif($invoice_collection->sum('amount') != $amount_refunded) {
|
||||
|
||||
$refund_text = "A partial refund was processed for Payment #{$payment_hash->payment->number}. <br><br> This payment is associated with multiple invoices, so you will need to manually apply the refund to the correct invoice/s.";
|
||||
|
||||
App::setLocale($payment_hash->payment->company->getLocale());
|
||||
|
||||
$mo = new EmailObject();
|
||||
$mo->subject = "Refund processed in Stripe for multiple invoices, action required.";
|
||||
$mo->body = $refund_text;
|
||||
$mo->text_body = $refund_text;
|
||||
$mo->company_key = $payment_hash->payment->company->company_key;
|
||||
$mo->html_template = 'email.template.generic';
|
||||
$mo->to = [new Address($payment_hash->payment->company->owner()->email, $payment_hash->payment->company->owner()->present()->name())];
|
||||
|
||||
Email::dispatch($mo, $payment_hash->payment->company);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
$invoices = $invoice_collection->toArray();
|
||||
@ -131,20 +152,21 @@ class ChargeRefunded implements ShouldQueue
|
||||
'date' => now()->format('Y-m-d'),
|
||||
'gateway_refund' => false,
|
||||
'email_receipt' => false,
|
||||
'via_webhook' => true,
|
||||
];
|
||||
|
||||
nlog($data);
|
||||
|
||||
$payment->refund($data);
|
||||
|
||||
$payment->private_notes .= 'Refunded via Stripe';
|
||||
return;
|
||||
}
|
||||
$payment->private_notes .= 'Refunded via Stripe ';
|
||||
|
||||
$payment->saveQuietly();
|
||||
|
||||
}
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [new WithoutOverlapping($this->company_gateway_id)];
|
||||
return [new WithoutOverlapping($this->company_key)];
|
||||
}
|
||||
}
|
||||
|
@ -12,54 +12,55 @@
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Exceptions\StripeConnectFailure;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Stripe\ACH;
|
||||
use App\PaymentDrivers\Stripe\ACSS;
|
||||
use App\PaymentDrivers\Stripe\Alipay;
|
||||
use App\PaymentDrivers\Stripe\BACS;
|
||||
use App\PaymentDrivers\Stripe\Bancontact;
|
||||
use App\PaymentDrivers\Stripe\BankTransfer;
|
||||
use App\PaymentDrivers\Stripe\BECS;
|
||||
use App\PaymentDrivers\Stripe\BrowserPay;
|
||||
use App\PaymentDrivers\Stripe\Charge;
|
||||
use App\PaymentDrivers\Stripe\Connect\Verify;
|
||||
use App\PaymentDrivers\Stripe\CreditCard;
|
||||
use App\PaymentDrivers\Stripe\EPS;
|
||||
use App\PaymentDrivers\Stripe\FPX;
|
||||
use App\PaymentDrivers\Stripe\GIROPAY;
|
||||
use App\PaymentDrivers\Stripe\iDeal;
|
||||
use App\PaymentDrivers\Stripe\ImportCustomers;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentWebhook;
|
||||
use App\PaymentDrivers\Stripe\Klarna;
|
||||
use App\PaymentDrivers\Stripe\PRZELEWY24;
|
||||
use App\PaymentDrivers\Stripe\SEPA;
|
||||
use App\PaymentDrivers\Stripe\SOFORT;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Exception;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Laracasts\Presenter\Exceptions\PresenterException;
|
||||
use Stripe\Stripe;
|
||||
use Stripe\Account;
|
||||
use Stripe\Customer;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use Stripe\SetupIntent;
|
||||
use Stripe\StripeClient;
|
||||
use App\Models\SystemLog;
|
||||
use Stripe\PaymentIntent;
|
||||
use Stripe\PaymentMethod;
|
||||
use Stripe\SetupIntent;
|
||||
use Stripe\Stripe;
|
||||
use Stripe\StripeClient;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\PaymentDrivers\Stripe\ACH;
|
||||
use App\PaymentDrivers\Stripe\EPS;
|
||||
use App\PaymentDrivers\Stripe\FPX;
|
||||
use App\PaymentDrivers\Stripe\ACSS;
|
||||
use App\PaymentDrivers\Stripe\BACS;
|
||||
use App\PaymentDrivers\Stripe\BECS;
|
||||
use App\PaymentDrivers\Stripe\SEPA;
|
||||
use App\PaymentDrivers\Stripe\iDeal;
|
||||
use App\PaymentDrivers\Stripe\Alipay;
|
||||
use App\PaymentDrivers\Stripe\Charge;
|
||||
use App\PaymentDrivers\Stripe\Klarna;
|
||||
use App\PaymentDrivers\Stripe\SOFORT;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use App\PaymentDrivers\Stripe\GIROPAY;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
use App\Exceptions\StripeConnectFailure;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use App\PaymentDrivers\Stripe\Bancontact;
|
||||
use App\PaymentDrivers\Stripe\BrowserPay;
|
||||
use App\PaymentDrivers\Stripe\CreditCard;
|
||||
use App\PaymentDrivers\Stripe\PRZELEWY24;
|
||||
use App\PaymentDrivers\Stripe\BankTransfer;
|
||||
use App\PaymentDrivers\Stripe\Connect\Verify;
|
||||
use App\PaymentDrivers\Stripe\ImportCustomers;
|
||||
use App\PaymentDrivers\Stripe\Jobs\ChargeRefunded;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use Laracasts\Presenter\Exceptions\PresenterException;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook;
|
||||
|
||||
class StripePaymentDriver extends BaseDriver
|
||||
{
|
||||
@ -670,31 +671,39 @@ class StripePaymentDriver extends BaseDriver
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request)
|
||||
{
|
||||
nlog($request->all());
|
||||
|
||||
if ($request->type === 'customer.source.updated') {
|
||||
$ach = new ACH($this);
|
||||
$ach->updateBankAccount($request->all());
|
||||
}
|
||||
|
||||
if ($request->type === 'payment_intent.processing') {
|
||||
PaymentIntentProcessingWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(10, 12)));
|
||||
PaymentIntentProcessingWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(5));
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
//payment_intent.succeeded - this will confirm or cancel the payment
|
||||
if ($request->type === 'payment_intent.succeeded') {
|
||||
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(10, 15)));
|
||||
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(5));
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
if ($request->type === 'payment_intent.partially_funded') {
|
||||
PaymentIntentPartiallyFundedWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(10, 15)));
|
||||
PaymentIntentPartiallyFundedWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(5));
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
if (in_array($request->type, ['payment_intent.payment_failed', 'charge.failed'])) {
|
||||
PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(5, 10)));
|
||||
PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(2));
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
if ($request->type === 'charge.refunded' && $request->data['object']['status'] == 'succeeded') {
|
||||
ChargeRefunded::dispatch($request->data, $request->company_key)->delay(now()->addSeconds(5));
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
@ -702,7 +711,6 @@ class StripePaymentDriver extends BaseDriver
|
||||
if ($request->type === 'charge.succeeded') {
|
||||
foreach ($request->data as $transaction) {
|
||||
|
||||
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $this->company_gateway->company_id)
|
||||
->where(function ($query) use ($transaction) {
|
||||
|
@ -192,6 +192,7 @@ class PaymentMethod
|
||||
'label' => ctrans('texts.apply_credit'),
|
||||
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
|
||||
'gateway_type_id' => GatewayType::CREDIT,
|
||||
'is_paypal' => false,
|
||||
];
|
||||
}
|
||||
|
||||
@ -210,12 +211,14 @@ class PaymentMethod
|
||||
'label' => $gateway->getConfigField('name').$fee_label,
|
||||
'company_gateway_id' => $gateway->id,
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
'is_paypal' => $gateway->isPayPal(),
|
||||
];
|
||||
} else {
|
||||
$this->payment_urls[] = [
|
||||
'label' => $gateway->getTypeAlias($type).$fee_label,
|
||||
'company_gateway_id' => $gateway->id,
|
||||
'gateway_type_id' => $type,
|
||||
'is_paypal' => $gateway->isPayPal(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -236,12 +239,14 @@ class PaymentMethod
|
||||
'label' => $gateway->getConfigField('name').$fee_label,
|
||||
'company_gateway_id' => $gateway_id,
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
'is_paypal' => $gateway->isPayPal(),
|
||||
];
|
||||
} else {
|
||||
$this->payment_urls[] = [
|
||||
'label' => $gateway->getTypeAlias($gateway_type_id).$fee_label,
|
||||
'company_gateway_id' => $gateway_id,
|
||||
'gateway_type_id' => $gateway_type_id,
|
||||
'is_paypal' => $gateway->isPayPal(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -259,6 +264,7 @@ class PaymentMethod
|
||||
'label' => ctrans('texts.apply_credit'),
|
||||
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
|
||||
'gateway_type_id' => GatewayType::CREDIT,
|
||||
'is_paypal' => false,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -44,17 +44,16 @@ class InstantPayment
|
||||
|
||||
public function run()
|
||||
{
|
||||
nlog($this->request->all());
|
||||
|
||||
/** @var \App\Models\ClientContact $cc */
|
||||
|
||||
$cc = auth()->guard('contact')->user();
|
||||
|
||||
$cc->first_name = $this->request->contact_first_name;
|
||||
$cc->last_name = $this->request->contact_last_name;
|
||||
$cc->email = $this->request->contact_email;
|
||||
|
||||
$cc->save();
|
||||
$cc->client->postal_code = strlen($cc->client->postal_code ?? '') > 1 ? $cc->client->postal_code : $this->request->client_postal_code;
|
||||
$cc->client->city = strlen($cc->client->city ?? '') > 1 ? $cc->client->city : $this->request->client_city;
|
||||
$cc->client->shipping_postal_code = strlen($cc->client->shipping_postal_code ?? '') > 1 ? $cc->client->shipping_postal_code : $cc->client->postal_code;
|
||||
$cc->client->shipping_city = strlen($cc->client->shipping_city ?? '') > 1 ? $cc->client->shipping_city : $cc->client->city;
|
||||
$cc->pushQuietly();
|
||||
|
||||
$is_credit_payment = false;
|
||||
|
||||
@ -73,8 +72,6 @@ class InstantPayment
|
||||
*/
|
||||
$payable_invoices = collect($this->request->payable_invoices);
|
||||
|
||||
nlog($payable_invoices);
|
||||
|
||||
$invoices = Invoice::query()->whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))->withTrashed()->get();
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
|
@ -42,6 +42,9 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
public function __construct(private Invoice $invoice, protected string $db)
|
||||
{
|
||||
|
||||
$this->client = $this->invoice->client;
|
||||
|
||||
}
|
||||
|
||||
public function run()
|
||||
@ -49,8 +52,7 @@ class AutoBillInvoice extends AbstractService
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
/* @var \App\Modesl\Client $client */
|
||||
$this->client = $this->invoice->client;
|
||||
|
||||
|
||||
$is_partial = false;
|
||||
|
||||
/* Is the invoice payable? */
|
||||
@ -272,7 +274,7 @@ class AutoBillInvoice extends AbstractService
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function applyUnappliedPayment(): self
|
||||
public function applyUnappliedPayment(): self
|
||||
{
|
||||
$unapplied_payments = Payment::query()
|
||||
->where('client_id', $this->client->id)
|
||||
@ -284,6 +286,11 @@ class AutoBillInvoice extends AbstractService
|
||||
->get();
|
||||
|
||||
$available_unapplied_balance = $unapplied_payments->sum('amount') - $unapplied_payments->sum('applied');
|
||||
|
||||
nlog($this->client->id);
|
||||
nlog($this->invoice->id);
|
||||
nlog($unapplied_payments->sum('amount'));
|
||||
nlog($unapplied_payments->sum('applied'));
|
||||
|
||||
nlog("available unapplied balance = {$available_unapplied_balance}");
|
||||
|
||||
@ -347,7 +354,7 @@ class AutoBillInvoice extends AbstractService
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
private function applyCreditPayment(): self
|
||||
public function applyCreditPayment(): self
|
||||
{
|
||||
$available_credits = Credit::query()->where('client_id', $this->client->id)
|
||||
->where('is_deleted', false)
|
||||
|
@ -44,7 +44,6 @@ class RefundPayment
|
||||
->setStatus() //sets status of payment
|
||||
->updatePaymentables() //update the paymentable items
|
||||
->adjustInvoices()
|
||||
->finalize()
|
||||
->save();
|
||||
|
||||
if (array_key_exists('email_receipt', $this->refund_data) && $this->refund_data['email_receipt'] == 'true') {
|
||||
@ -52,10 +51,11 @@ class RefundPayment
|
||||
EmailRefundPayment::dispatch($this->payment, $this->payment->company, $contact);
|
||||
}
|
||||
|
||||
$notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : ";
|
||||
$notes .= $this->refund_data['gateway_refund'] !== false ? ctrans('texts.yes') : ctrans('texts.no');
|
||||
|
||||
$is_gateway_refund = ($this->refund_data['gateway_refund'] !== false || $this->refund_failed || (isset($this->refund_data['via_webhook']) && $this->refund_data['via_webhook'] !== false)) ? ctrans('texts.yes') : ctrans('texts.no');
|
||||
$notes = ctrans('texts.refunded') . " : {$this->total_refund} - " . ctrans('texts.gateway_refund') . " : " . $is_gateway_refund;
|
||||
|
||||
$this->createActivity($notes);
|
||||
$this->finalize();
|
||||
|
||||
return $this->payment;
|
||||
}
|
||||
@ -178,7 +178,7 @@ class RefundPayment
|
||||
*/
|
||||
private function setStatus()
|
||||
{
|
||||
if ($this->total_refund == $this->payment->amount) {
|
||||
if ($this->total_refund == $this->payment->amount || floatval($this->payment->amount) == floatval($this->payment->refunded)) {
|
||||
$this->payment->status_id = Payment::STATUS_REFUNDED;
|
||||
} else {
|
||||
$this->payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED;
|
||||
|
@ -88,7 +88,7 @@ class SubscriptionService
|
||||
|
||||
|
||||
// if we have a recurring product - then generate a recurring invoice
|
||||
if (strlen($this->subscription->recurring_product_ids) >= 1) {
|
||||
if (strlen($this->subscription->recurring_product_ids ?? '') >= 1) {
|
||||
if (isset($payment_hash->data->billing_context->bundle)) {
|
||||
$recurring_invoice = $this->convertInvoiceToRecurringBundle($payment_hash->payment->client_id, $payment_hash->data->billing_context->bundle);
|
||||
} else {
|
||||
@ -1024,10 +1024,10 @@ class SubscriptionService
|
||||
$invoice->subscription_id = $this->subscription->id;
|
||||
$invoice->is_proforma = true;
|
||||
|
||||
if (strlen($data['coupon']) >= 1 && ($data['coupon'] == $this->subscription->promo_code) && $this->subscription->promo_discount > 0) {
|
||||
if (strlen($data['coupon'] ?? '') >= 1 && ($data['coupon'] == $this->subscription->promo_code) && $this->subscription->promo_discount > 0) {
|
||||
$invoice->discount = $this->subscription->promo_discount;
|
||||
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
} elseif (strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0) {
|
||||
} elseif (strlen($this->subscription->promo_code ?? '') == 0 && $this->subscription->promo_discount > 0) {
|
||||
$invoice->discount = $this->subscription->promo_discount;
|
||||
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||
}
|
||||
@ -1118,7 +1118,7 @@ class SubscriptionService
|
||||
*/
|
||||
public function triggerWebhook($context)
|
||||
{
|
||||
if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) { //@phpstan-ignore-line
|
||||
if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url'] ?? '') < 1) { //@phpstan-ignore-line
|
||||
return ["message" => "Success", "status_code" => 200];
|
||||
}
|
||||
|
||||
@ -1436,7 +1436,7 @@ class SubscriptionService
|
||||
*/
|
||||
public function handleNoPaymentFlow(Invoice $invoice, $bundle, ClientContact $contact)
|
||||
{
|
||||
if (strlen($this->subscription->recurring_product_ids) >= 1) {
|
||||
if (strlen($this->subscription->recurring_product_ids ?? '') >= 1) {
|
||||
$recurring_invoice = $this->convertInvoiceToRecurringBundle($contact->client_id, collect($bundle)->map(function ($bund) {
|
||||
return (object) $bund;
|
||||
}));
|
||||
@ -1492,7 +1492,7 @@ class SubscriptionService
|
||||
*/
|
||||
private function handleRedirect($default_redirect)
|
||||
{
|
||||
if (array_key_exists('return_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['return_url']) >= 1) {
|
||||
if (array_key_exists('return_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['return_url'] ?? '') >= 1) {
|
||||
return method_exists(redirect(), "send") ? redirect($this->subscription->webhook_configuration['return_url'])->send() : redirect($this->subscription->webhook_configuration['return_url']);
|
||||
}
|
||||
|
||||
|
@ -1023,7 +1023,8 @@ class TemplateService
|
||||
'vat_number' => $project->client->vat_number ?? '',
|
||||
'currency' => $project->client->currency()->code ?? 'USD',
|
||||
] : [],
|
||||
'user' => $this->userInfo($project->user)
|
||||
'user' => $this->userInfo($project->user),
|
||||
'invoices' => $this->processInvoices($project->invoices)
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION', '5.10.13'),
|
||||
'app_tag' => env('APP_TAG', '5.10.13'),
|
||||
'app_version' => env('APP_VERSION', '5.10.16'),
|
||||
'app_tag' => env('APP_TAG', '5.10.16'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
@ -5124,7 +5124,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.',
|
||||
|
9
public/build/assets/payment-1bdbd169.js
vendored
9
public/build/assets/payment-1bdbd169.js
vendored
@ -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 s{constructor(t,e,a){this.shouldDisplayTerms=t,this.shouldDisplaySignature=e,this.shouldDisplayRff=a,this.submitting=!1,this.steps=new Map,this.shouldDisplayRff&&this.steps.set("rff",{element:document.getElementById("displayRequiredFieldsModal"),nextButton:document.getElementById("rff-next-step"),callback:()=>{const n={firstName:document.querySelector('input[name="rff_first_name"]'),lastName:document.querySelector('input[name="rff_last_name"]'),email:document.querySelector('input[name="rff_email"]')};n.firstName&&(document.querySelector('input[name="contact_first_name"]').value=n.firstName.value),n.lastName&&(document.querySelector('input[name="contact_last_name"]').value=n.lastName.value),n.email&&(document.querySelector('input[name="contact_email"]').value=n.email.value)}}),this.shouldDisplaySignature&&this.steps.set("signature",{element:document.getElementById("displaySignatureModal"),nextButton:document.getElementById("signature-next-step"),boot:()=>this.signaturePad=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"}),callback:()=>document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL()}),this.shouldDisplayTerms&&this.steps.set("terms",{element:document.getElementById("displayTermsModal"),nextButton:document.getElementById("accept-terms-button")})}handleMethodSelect(t){if(document.getElementById("company_gateway_id").value=t.dataset.companyGatewayId,document.getElementById("payment_method_id").value=t.dataset.gatewayTypeId,this.steps.size===0)return this.submitForm();const e=this.steps.values().next().value;e.element.removeAttribute("style"),e.boot&&e.boot(),console.log(e),e.nextButton.addEventListener("click",()=>{e.element.setAttribute("style","display: none;"),this.steps=new Map(Array.from(this.steps.entries()).slice(1)),e.callback&&e.callback(),this.handleMethodSelect(t)})}submitForm(){this.submitting=!0,document.getElementById("payment-form").submit()}handle(){document.querySelectorAll(".dropdown-gateway-button").forEach(t=>{t.addEventListener("click",()=>{this.submitting||this.handleMethodSelect(t)})})}}const i=document.querySelector('meta[name="require-invoice-signature"]').content,o=document.querySelector('meta[name="show-invoice-terms"]').content,l=document.querySelector('meta[name="show-required-fields-form"]').content;new s(!!+o,!!+i,!!+l).handle();
|
9
public/build/assets/payment-292ee4d0.js
vendored
Normal file
9
public/build/assets/payment-292ee4d0.js
vendored
Normal file
@ -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 a{constructor(t,n){this.shouldDisplayTerms=t,this.shouldDisplaySignature=n,this.submitting=!1,this.steps=new Map,this.steps.set("rff",{element:document.getElementById("displayRequiredFieldsModal"),nextButton:document.getElementById("rff-next-step"),callback:()=>{const e={firstName:document.querySelector('input[name="rff_first_name"]'),lastName:document.querySelector('input[name="rff_last_name"]'),email:document.querySelector('input[name="rff_email"]'),city:document.querySelector('input[name="rff_city"]'),postalCode:document.querySelector('input[name="rff_postal_code"]')};e.firstName&&(document.querySelector('input[name="contact_first_name"]').value=e.firstName.value),e.lastName&&(document.querySelector('input[name="contact_last_name"]').value=e.lastName.value),e.email&&(document.querySelector('input[name="contact_email"]').value=e.email.value),e.city&&(document.querySelector('input[name="client_city"]').value=e.city.value),e.postalCode&&(document.querySelector('input[name="client_postal_code"]').value=e.postalCode.value)}}),this.shouldDisplaySignature&&this.steps.set("signature",{element:document.getElementById("displaySignatureModal"),nextButton:document.getElementById("signature-next-step"),boot:()=>this.signaturePad=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"}),callback:()=>document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL()}),this.shouldDisplayTerms&&this.steps.set("terms",{element:document.getElementById("displayTermsModal"),nextButton:document.getElementById("accept-terms-button")})}handleMethodSelect(t){document.getElementById("company_gateway_id").value=t.dataset.companyGatewayId,document.getElementById("payment_method_id").value=t.dataset.gatewayTypeId;const n=document.querySelector('input[name="contact_first_name"').value.length>=1&&document.querySelector('input[name="contact_last_name"').value.length>=1&&document.querySelector('input[name="contact_email"').value.length>=1&&document.querySelector('input[name="client_city"').value.length>=1&&document.querySelector('input[name="client_postal_code"').value.length>=1;if((t.dataset.isPaypal!="1"||n)&&this.steps.delete("rff"),this.steps.size===0)return this.submitForm();const e=this.steps.values().next().value;e.element.removeAttribute("style"),e.boot&&e.boot(),console.log(e),e.nextButton.addEventListener("click",()=>{e.element.setAttribute("style","display: none;"),this.steps=new Map(Array.from(this.steps.entries()).slice(1)),e.callback&&e.callback(),this.handleMethodSelect(t)})}submitForm(){this.submitting=!0,document.getElementById("payment-form").submit()}handle(){document.querySelectorAll(".dropdown-gateway-button").forEach(t=>{t.addEventListener("click",()=>{this.submitting||this.handleMethodSelect(t)})})}}const l=document.querySelector('meta[name="require-invoice-signature"]').content,o=document.querySelector('meta[name="show-invoice-terms"]').content;new a(!!+o,!!+l).handle();
|
@ -23,7 +23,7 @@
|
||||
"src": "resources/js/clients/invoices/action-selectors.js"
|
||||
},
|
||||
"resources/js/clients/invoices/payment.js": {
|
||||
"file": "assets/payment-1bdbd169.js",
|
||||
"file": "assets/payment-292ee4d0.js",
|
||||
"isEntry": true,
|
||||
"src": "resources/js/clients/invoices/payment.js"
|
||||
},
|
||||
@ -240,7 +240,7 @@
|
||||
"src": "resources/js/setup/setup.js"
|
||||
},
|
||||
"resources/sass/app.scss": {
|
||||
"file": "assets/app-02bc3b96.css",
|
||||
"file": "assets/app-039bd735.css",
|
||||
"isEntry": true,
|
||||
"src": "resources/sass/app.scss"
|
||||
}
|
||||
|
77
resources/js/clients/invoices/payment.js
vendored
77
resources/js/clients/invoices/payment.js
vendored
@ -9,39 +9,47 @@
|
||||
*/
|
||||
|
||||
class Payment {
|
||||
constructor(displayTerms, displaySignature, displayRff) {
|
||||
constructor(displayTerms, displaySignature) {
|
||||
this.shouldDisplayTerms = displayTerms;
|
||||
this.shouldDisplaySignature = displaySignature;
|
||||
this.shouldDisplayRff = displayRff;
|
||||
|
||||
|
||||
this.submitting = false;
|
||||
this.steps = new Map()
|
||||
|
||||
if (this.shouldDisplayRff) {
|
||||
this.steps.set("rff", {
|
||||
element: document.getElementById('displayRequiredFieldsModal'),
|
||||
nextButton: document.getElementById('rff-next-step'),
|
||||
callback: () => {
|
||||
const fields = {
|
||||
firstName: document.querySelector('input[name="rff_first_name"]'),
|
||||
lastName: document.querySelector('input[name="rff_last_name"]'),
|
||||
email: document.querySelector('input[name="rff_email"]'),
|
||||
}
|
||||
|
||||
if (fields.firstName) {
|
||||
document.querySelector('input[name="contact_first_name"]').value = fields.firstName.value;
|
||||
}
|
||||
|
||||
if (fields.lastName) {
|
||||
document.querySelector('input[name="contact_last_name"]').value = fields.lastName.value;
|
||||
}
|
||||
|
||||
if (fields.email) {
|
||||
document.querySelector('input[name="contact_email"]').value = fields.email.value;
|
||||
}
|
||||
this.steps.set("rff", {
|
||||
element: document.getElementById('displayRequiredFieldsModal'),
|
||||
nextButton: document.getElementById('rff-next-step'),
|
||||
callback: () => {
|
||||
const fields = {
|
||||
firstName: document.querySelector('input[name="rff_first_name"]'),
|
||||
lastName: document.querySelector('input[name="rff_last_name"]'),
|
||||
email: document.querySelector('input[name="rff_email"]'),
|
||||
city: document.querySelector('input[name="rff_city"]'),
|
||||
postalCode: document.querySelector('input[name="rff_postal_code"]'),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (fields.firstName) {
|
||||
document.querySelector('input[name="contact_first_name"]').value = fields.firstName.value;
|
||||
}
|
||||
|
||||
if (fields.lastName) {
|
||||
document.querySelector('input[name="contact_last_name"]').value = fields.lastName.value;
|
||||
}
|
||||
|
||||
if (fields.email) {
|
||||
document.querySelector('input[name="contact_email"]').value = fields.email.value;
|
||||
}
|
||||
|
||||
if (fields.city) {
|
||||
document.querySelector('input[name="client_city"]').value = fields.city.value;
|
||||
}
|
||||
|
||||
if (fields.postalCode) {
|
||||
document.querySelector('input[name="client_postal_code"]').value = fields.postalCode.value;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if (this.shouldDisplaySignature) {
|
||||
this.steps.set("signature", {
|
||||
@ -71,7 +79,17 @@ class Payment {
|
||||
element.dataset.companyGatewayId;
|
||||
document.getElementById("payment_method_id").value =
|
||||
element.dataset.gatewayTypeId;
|
||||
|
||||
|
||||
const filledRff = document.querySelector('input[name="contact_first_name"').value.length >=1 &&
|
||||
document.querySelector('input[name="contact_last_name"').value.length >= 1 &&
|
||||
document.querySelector('input[name="contact_email"').value.length >= 1 &&
|
||||
document.querySelector('input[name="client_city"').value.length >= 1 &&
|
||||
document.querySelector('input[name="client_postal_code"').value.length >= 1;
|
||||
|
||||
if (element.dataset.isPaypal != '1' || filledRff) {
|
||||
this.steps.delete("rff");
|
||||
}
|
||||
|
||||
if (this.steps.size === 0) {
|
||||
return this.submitForm();
|
||||
}
|
||||
@ -124,6 +142,5 @@ const signature = document.querySelector(
|
||||
).content;
|
||||
|
||||
const terms = document.querySelector('meta[name="show-invoice-terms"]').content;
|
||||
const rff = document.querySelector('meta[name="show-required-fields-form"]').content;
|
||||
|
||||
new Payment(Boolean(+terms), Boolean(+signature), Boolean(+rff)).handle();
|
||||
new Payment(Boolean(+terms), Boolean(+signature)).handle();
|
||||
|
@ -141,12 +141,15 @@
|
||||
<input type="hidden" name="contact_first_name" value="{{ $contact->first_name }}">
|
||||
<input type="hidden" name="contact_last_name" value="{{ $contact->last_name }}">
|
||||
<input type="hidden" name="contact_email" value="{{ $contact->email }}">
|
||||
<input type="hidden" name="client_city" value="{{ $contact->client->city }}">
|
||||
<input type="hidden" name="client_postal_code" value="{{ $contact->client->postal_code }}">
|
||||
|
||||
</form>
|
||||
|
||||
@if($steps['started_payment'] == false)
|
||||
@foreach($this->methods as $method)
|
||||
<button
|
||||
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}'); $wire.$refresh(); "
|
||||
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}', '{{ $method['is_paypal'] }}'); $wire.$refresh(); "
|
||||
class="px-3 py-2 border rounded mr-4 hover:border-blue-600">
|
||||
{{ $method['label'] }}
|
||||
</button>
|
||||
@ -189,27 +192,41 @@
|
||||
<form wire:submit="handleRff">
|
||||
@csrf
|
||||
|
||||
@if(strlen($contact->first_name) === 0)
|
||||
@if(strlen($contact->first_name ?? '') === 0)
|
||||
<div class="col-auto mt-3">
|
||||
<label for="first_name" class="input-label">{{ ctrans('texts.first_name') }}</label>
|
||||
<input id="first_name" class="input w-full" wire:model="contact_first_name" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($contact->last_name) === 0)
|
||||
@if(strlen($contact->last_name ?? '') === 0)
|
||||
<div class="col-auto mt-3 @if($contact->last_name) !== 0) hidden @endif">
|
||||
<label for="last_name" class="input-label">{{ ctrans('texts.last_name') }}</label>
|
||||
<input id="last_name" class="input w-full" wire:model="contact_last_name" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($contact->email) === 0)
|
||||
@if(strlen($contact->email ?? '') === 0)
|
||||
<div class="col-auto mt-3 @if($contact->email) !== 0) hidden @endif">
|
||||
<label for="email" class="input-label">{{ ctrans('texts.email') }}</label>
|
||||
<input id="email" class="input w-full" wire:model="contact_email" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($client_postal_code ?? '') === 0)
|
||||
<div class="col-auto mt-3 @if($client_postal_code) !== 0) hidden @endif">
|
||||
<label for="postal_code" class="input-label">{{ ctrans('texts.postal_code') }}</label>
|
||||
<input id="postal_code" class="input w-full" wire:model="client_postal_code" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($client_city ?? '') === 0)
|
||||
<div class="col-auto mt-3 @if($client_city) !== 0) hidden @endif">
|
||||
<label for="city" class="input-label">{{ ctrans('texts.city') }}</label>
|
||||
<input id="city" class="input w-full" wire:model="client_city" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="button button-block bg-primary text-white mt-4">
|
||||
|
@ -21,6 +21,11 @@
|
||||
<input type="hidden" name="action" value="payment">
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}"/>
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"/>
|
||||
<input type="hidden" name="contact_first_name" value="{{ $contact ? $contact->first_name : '' }}">
|
||||
<input type="hidden" name="contact_last_name" value="{{ $contact ? $contact->last_name : '' }}">
|
||||
<input type="hidden" name="contact_email" value="{{ $contact ? $contact->email : '' }}">
|
||||
<input type="hidden" name="client_city" value="{{ $contact ? $contact->client->city : '' }}">
|
||||
<input type="hidden" name="client_postal_code" value="{{ $contact ? $contact->client->postal_code : '' }}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -313,12 +318,67 @@
|
||||
{{ ctrans('texts.trial_call_to_action') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@elseif(count($methods) > 0 && $check_rff)
|
||||
|
||||
@if($errors->any())
|
||||
<div class="w-full mx-auto text-center bg-red-100 border border-red-400 text-red-700 px-4 py-1 rounded">
|
||||
@foreach($errors->all() as $error)
|
||||
<p class="w-full">{{ $error }}</p>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
<form wire:submit="handleRff">
|
||||
@csrf
|
||||
|
||||
@if(strlen($contact->first_name ?? '') === 0)
|
||||
<div class="col-auto mt-3 flex items-center space-x-0 @if($contact->first_name) !== 0) hidden @endif">
|
||||
<label for="first_name" class="w-1/4 text-sm font-medium text-white whitespace-nowrap text-left">{{ ctrans('texts.first_name') }}</label>
|
||||
<input id="first_name" class="w-3/4 rounded-md border-gray-300 pl-2 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm text-gray-700" wire:model="contact_first_name" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($contact->last_name ?? '') === 0)
|
||||
<div class="col-auto mt-3 flex items-center space-x-0 @if($contact->last_name) !== 0) hidden @endif">
|
||||
<label for="last_name" class="w-1/4 text-sm font-medium text-white whitespace-nowrap text-left">{{ ctrans('texts.last_name') }}</label>
|
||||
<input id="last_name" class="w-3/4 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm text-gray-700" wire:model="contact_last_name" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($contact->email ?? '') === 0)
|
||||
<div class="col-auto mt-3 flex items-center space-x-0 @if($contact->email) !== 0) hidden @endif">
|
||||
<label for="email" class="w-1/4 text-sm font-medium text-white whitespace-nowrap text-left">{{ ctrans('texts.email') }}</label>
|
||||
<input id="email" class="w-3/4 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm text-gray-700" wire:model="contact_email" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($client_postal_code ?? '') === 0)
|
||||
<div class="col-auto mt-3 flex items-center space-x-0 @if($client_postal_code) !== 0) hidden @endif">
|
||||
<label for="postal_code" class="w-1/4 text-sm font-medium text-white whitespace-nowrap text-left">{{ ctrans('texts.postal_code') }}</label>
|
||||
<input id="postal_code" class="w-3/4 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm text-gray-700" wire:model="client_postal_code" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen($client_city ?? '') === 0)
|
||||
<div class="col-auto mt-3 flex items-center space-x-0 @if($client_city) !== 0) hidden @endif">
|
||||
<label for="city" class="w-1/4 text-sm font-medium text-white whitespace-nowrap text-left">{{ ctrans('texts.city') }}</label>
|
||||
<input id="city" class="w-3/4 rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm text-gray-700" wire:model="client_city" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="relative -ml-px inline-flex items-center space-x-2 rounded border border-gray-300 bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 mt-4">
|
||||
{{ ctrans('texts.next') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@elseif(count($methods) > 0)
|
||||
<div class="mt-4" x-show.important="!toggle" x-transition>
|
||||
@foreach($methods as $method)
|
||||
<button
|
||||
x-on:click="buttonDisabled = true" x-bind:disabled="buttonDisabled"
|
||||
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}')"
|
||||
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}', '{{ $method['is_paypal'] }}')"
|
||||
class="relative -ml-px inline-flex items-center space-x-2 rounded border border-gray-300 bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
||||
{{ $method['label'] }}
|
||||
</button>
|
||||
|
@ -23,6 +23,7 @@
|
||||
<a href="#" @click="open = false" dusk="pay-with-custom"
|
||||
data-company-gateway-id="{{ $method['company_gateway_id'] }}"
|
||||
data-gateway-type-id="{{ $method['gateway_type_id'] }}"
|
||||
data-is-paypal="{{ $method['is_paypal'] }}"
|
||||
class="block px-4 py-2 text-sm leading-5 text-gray-700 dropdown-gateway-button hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
|
||||
dusk="payment-method">
|
||||
{{ \App\Models\CompanyGateway::find($method['company_gateway_id'])->firstOrFail()->getConfigField('name') }}
|
||||
@ -31,6 +32,7 @@
|
||||
<a href="#" @click="open = false" dusk="pay-with-{{ $index }}"
|
||||
data-company-gateway-id="{{ $method['company_gateway_id'] }}"
|
||||
data-gateway-type-id="{{ $method['gateway_type_id'] }}"
|
||||
data-is-paypal="{{ $method['is_paypal'] }}"
|
||||
class="block px-4 py-2 text-sm leading-5 text-gray-700 dropdown-gateway-button hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
|
||||
dusk="payment-method">
|
||||
{{ $method['label'] }}
|
||||
|
@ -83,7 +83,7 @@
|
||||
@if(!$state['payment_initialised'])
|
||||
@foreach($this->methods as $method)
|
||||
<button
|
||||
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}'); $wire.$refresh();"
|
||||
wire:click="handleMethodSelectingEvent('{{ $method['company_gateway_id'] }}', '{{ $method['gateway_type_id'] }}', '{{ $method['is_paypal'] }}'); $wire.$refresh();"
|
||||
class="px-3 py-2 border bg-white rounded mr-4 hover:border-blue-600">
|
||||
{{ $method['label'] }}
|
||||
</button>
|
||||
|
@ -2,7 +2,7 @@
|
||||
style="display: none"
|
||||
id="displayRequiredFieldsModal"
|
||||
class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center"
|
||||
x-data="{ open: true }"
|
||||
x-data="formValidation()"
|
||||
>
|
||||
<div
|
||||
x-show="open"
|
||||
@ -45,56 +45,120 @@
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.details') }}
|
||||
</h3>
|
||||
<div class="mt-2">
|
||||
@if(strlen(auth()->guard('contact')->user()->first_name) === 0)
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="first_name" class="input-label">{{ ctrans('texts.first_name') }}</label>
|
||||
<input id="first_name" class="input w-full" name="rff_first_name" value="{{ auth()->guard('contact')->user()->first_name }}" />
|
||||
<label for="rff_first_name" class="input-label">{{ ctrans('texts.first_name') }}</label>
|
||||
<input
|
||||
id="rff_first_name"
|
||||
class="input w-full"
|
||||
name="rff_first_name"
|
||||
value="{{ auth()->guard('contact')->user()->first_name }}"
|
||||
x-model="rff_first_name"
|
||||
@blur="validateFirstName()"
|
||||
:class="{ 'border-red-500': errors.rff_first_name }"
|
||||
/>
|
||||
<span x-show="errors.rff_first_name" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_first_name"></span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen(auth()->guard('contact')->user()->last_name) === 0)
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="last_name" class="input-label">{{ ctrans('texts.last_name') }}</label>
|
||||
<input id="last_name" class="input w-full" name="rff_last_name" value="{{ auth()->guard('contact')->user()->last_name }}"/>
|
||||
<label for="rff_last_name" class="input-label">{{ ctrans('texts.last_name') }}</label>
|
||||
<input
|
||||
id="rff_last_name"
|
||||
class="input w-full"
|
||||
name="rff_last_name"
|
||||
x-model="rff_last_name"
|
||||
@blur="validateLastName()"
|
||||
:class="{ 'border-red-500': errors.rff_last_name }"
|
||||
/>
|
||||
<span x-show="errors.rff_last_name" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_last_name"></span>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen(auth()->guard('contact')->user()->email) === 0)
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="email" class="input-label">{{ ctrans('texts.email') }}</label>
|
||||
<input id="email" class="input w-full" name="rff_email" value="{{ auth()->guard('contact')->user()->email }}"/>
|
||||
<input
|
||||
id="rff_email"
|
||||
class="input w-full"
|
||||
name="rff_email"
|
||||
x-model="rff_email"
|
||||
@blur="validateEmail()"
|
||||
:class="{ 'border-red-500': errors.rff_email }"
|
||||
/>
|
||||
<span x-show="errors.rff_email" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_email"></span>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen(auth()->guard('contact')->user()->client->city) === 0)
|
||||
<div class="col-span-6 sm:col-span-3" id="rff_city">
|
||||
<label for="city" class="input-label">{{ ctrans('texts.city') }}</label>
|
||||
<input
|
||||
id="rff_city"
|
||||
class="input w-full"
|
||||
name="rff_city"
|
||||
x-model="rff_city"
|
||||
@blur="validateCity()"
|
||||
:class="{ 'border-red-500': errors.rff_city }"
|
||||
/>
|
||||
<span x-show="errors.rff_city" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_city"></span>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen(auth()->guard('contact')->user()->client->postal_code) === 0)
|
||||
<div class="col-span-6 sm:col-span-3" id="rff_postal_code">
|
||||
<label for="postal_code" class="input-label">{{ ctrans('texts.postal_code') }}</label>
|
||||
<input
|
||||
id="rff_postal_code"
|
||||
class="input w-full"
|
||||
name="rff_postal_code"
|
||||
x-model="rff_postal_code"
|
||||
@blur="validatePostalCode()"
|
||||
:class="{ 'border-red-500': errors.rff_postal_code }"
|
||||
/>
|
||||
<span x-show="errors.rff_postal_code" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_postal_code"></span>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse" >
|
||||
<div
|
||||
class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto"
|
||||
x-data
|
||||
>
|
||||
class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
|
||||
<button
|
||||
type="button"
|
||||
id="rff-next-step"
|
||||
@@click="validateForm"
|
||||
class="button button-primary bg-primary"
|
||||
>
|
||||
{{ ctrans('texts.next_step') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
id="rff-next-step"
|
||||
class="hidden">
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto"
|
||||
x-data
|
||||
|
||||
>
|
||||
<button
|
||||
@click="document.getElementById('displayRequiredFieldsModal').style.display = 'none';"
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
id="close-button"
|
||||
@click="document.getElementById('displayRequiredFieldsModal').style.display = 'none';"
|
||||
>
|
||||
{{ ctrans('texts.close') }}
|
||||
</button>
|
||||
@ -102,3 +166,64 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
function formValidation() {
|
||||
|
||||
return {
|
||||
open: true,
|
||||
rff_last_name: '{{ auth()->guard('contact')->user()->last_name }}',
|
||||
rff_first_name: '{{ auth()->guard('contact')->user()->first_name }}',
|
||||
rff_email: '{{ auth()->guard('contact')->user()->email }}',
|
||||
rff_city: '{{ auth()->guard('contact')->user()->client->city }}',
|
||||
rff_postal_code: '{{ auth()->guard('contact')->user()->client->postal_code }}',
|
||||
errors: {
|
||||
rff_first_name: '',
|
||||
rff_last_name: '',
|
||||
rff_city: '',
|
||||
rff_postal_code: '',
|
||||
rff_email: ''
|
||||
},
|
||||
|
||||
validateFirstName() {
|
||||
this.errors.rff_first_name = this.rff_first_name.trim() === '' ? '{{ ctrans('texts.first_name') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
|
||||
},
|
||||
|
||||
validateLastName() {
|
||||
this.errors.rff_last_name = this.rff_last_name.trim() === '' ? '{{ ctrans('texts.last_name') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
|
||||
},
|
||||
|
||||
validateEmail() {
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
this.errors.rff_email = !emailPattern.test(this.rff_email.trim()) ? '{{ ctrans('texts.provide_email') }}' : '';
|
||||
},
|
||||
|
||||
validatePostalCode() {
|
||||
this.errors.rff_postal_code = this.rff_postal_code.trim() === '' ? '{{ ctrans('texts.postal_code') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
|
||||
},
|
||||
|
||||
validateCity() {
|
||||
this.errors.rff_city = this.rff_city.trim() === '' ? '{{ ctrans('texts.city') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
|
||||
},
|
||||
|
||||
validateForm() {
|
||||
|
||||
this.validateFirstName();
|
||||
this.validateLastName();
|
||||
this.validateEmail();
|
||||
this.validateCity();
|
||||
this.validatePostalCode();
|
||||
|
||||
if (!this.errors.rff_first_name && !this.errors.rff_last_name && !this.errors.email && !this.errors.rff_postal_code && !this.errors.rff_city) {
|
||||
|
||||
const next_rff = document.getElementById('rff-next-step');
|
||||
next_rff.click();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
@push('head')
|
||||
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<meta name="show-required-fields-form" content="{{ auth()->guard('contact')->user()->showRff() }}" />
|
||||
<script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
@ -22,6 +21,9 @@
|
||||
<input type="hidden" name="contact_last_name" value="{{ auth()->guard('contact')->user()->last_name }}">
|
||||
<input type="hidden" name="contact_email" value="{{ auth()->guard('contact')->user()->email }}">
|
||||
|
||||
<input type="hidden" name="client_city" value="{{ auth()->guard('contact')->user()->client->city }}">
|
||||
<input type="hidden" name="client_postal_code" value="{{ auth()->guard('contact')->user()->client->postal_code }}">
|
||||
|
||||
<div class="container mx-auto">
|
||||
<div class="grid grid-cols-6 gap-4">
|
||||
<div class="col-span-6 md:col-start-2 md:col-span-4">
|
||||
|
@ -4,7 +4,6 @@
|
||||
@push('head')
|
||||
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<meta name="show-required-fields-form" content="{{ auth()->guard('contact')->user()->showRff() }}" />
|
||||
@include('portal.ninja2020.components.no-cache')
|
||||
<script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script>
|
||||
|
||||
@ -47,6 +46,8 @@
|
||||
<input type="hidden" name="contact_first_name" value="{{ auth()->guard('contact')->user()->first_name }}">
|
||||
<input type="hidden" name="contact_last_name" value="{{ auth()->guard('contact')->user()->last_name }}">
|
||||
<input type="hidden" name="contact_email" value="{{ auth()->guard('contact')->user()->email }}">
|
||||
<input type="hidden" name="client_city" value="{{ auth()->guard('contact')->user()->client->city }}">
|
||||
<input type="hidden" name="client_postal_code" value="{{ auth()->guard('contact')->user()->client->postal_code }}">
|
||||
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4" translate>
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
|
@ -164,6 +164,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
|
||||
|
||||
Route::post('charts/totals_v2', [ChartController::class, 'totalsV2'])->name('chart.totals_v2');
|
||||
Route::post('charts/chart_summary_v2', [ChartController::class, 'chart_summaryV2'])->name('chart.chart_summary_v2');
|
||||
Route::post('charts/calculated_fields', [ChartController::class, 'calculatedFields'])->name('chart.calculated_fields');
|
||||
|
||||
Route::post('claim_license', [LicenseController::class, 'index'])->name('license.index');
|
||||
|
||||
|
@ -196,7 +196,7 @@ class WaveTest extends TestCase
|
||||
'import_type' => 'waveaccounting',
|
||||
];
|
||||
|
||||
Cache::put($hash.'-expense', base64_encode($csv), 360);
|
||||
Cache::put($hash.'-invoice', base64_encode($csv), 360);
|
||||
|
||||
$csv_importer = new Wave($data, $this->company);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user