1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 13:12:50 +01:00

Subscriptions v2

This commit is contained in:
David Bomba 2022-12-15 12:16:50 +11:00
parent b7316255a9
commit 55c0453b75
6 changed files with 143 additions and 55 deletions

View File

@ -196,6 +196,7 @@ class BillingPortalPurchasev2 extends Component
public $float_amount_total;
public $payment_started = false;
public $valid_coupon = false;
public $payable_invoices = [];
public function mount()
{
@ -310,12 +311,13 @@ class BillingPortalPurchasev2 extends Component
{
if($this->coupon == $this->subscription->promo_code) {
$this->buildBundle();
$this->valid_coupon = true;
$this->buildBundle();
}
else{
$this->discount = 0;
$this->valid_coupon = false;
$this->buildBundle();
}
}
@ -378,24 +380,24 @@ class BillingPortalPurchasev2 extends Component
return $k == $key;
});
$qty = isset($this->data[$key]['optional_recurring_qty']) ? $this->data[$key]['optional_recurring_qty'] : 0;
$qty = isset($this->data[$key]['optional_recurring_qty']) ? $this->data[$key]['optional_recurring_qty'] : false;
$total = $p->price * $qty;
if($qty == 0)
return;
if($qty)
{
$this->bundle->push([
'description' => $p->notes,
'product_key' => $p->product_key,
'unit_cost' => $p->price,
'product' => nl2br(substr($p->notes, 0, 50)),
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
'total' => $total,
'qty' => $qty,
'is_recurring' => true
]);
$this->bundle->push([
'description' => $p->notes,
'product_key' => $p->product_key,
'unit_cost' => $p->price,
'product' => nl2br(substr($p->notes, 0, 50)),
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
'total' => $total,
'qty' => $qty,
'is_recurring' => true
]);
}
}
/* Optional products can have a variable quantity */
@ -405,23 +407,23 @@ class BillingPortalPurchasev2 extends Component
return $k == $key;
});
$qty = isset($this->data[$key]['optional_qty']) ? $this->data[$key]['optional_qty'] : 0;
$qty = isset($this->data[$key]['optional_qty']) ? $this->data[$key]['optional_qty'] : false;
$total = $p->price * $qty;
if($qty == 0)
return;
$this->bundle->push([
'description' => $p->notes,
'product_key' => $p->product_key,
'unit_cost' => $p->price,
'product' => nl2br(substr($p->notes, 0, 50)),
'price' => Number::formatMoney($total, $this->subscription->company),
'total' => $total,
'qty' => $qty,
'is_recurring' => false
]);
if($qty)
{
$this->bundle->push([
'description' => $p->notes,
'product_key' => $p->product_key,
'unit_cost' => $p->price,
'product' => nl2br(substr($p->notes, 0, 50)),
'price' => Number::formatMoney($total, $this->subscription->company),
'total' => $total,
'qty' => $qty,
'is_recurring' => false
]);
}
}
}
@ -429,7 +431,7 @@ class BillingPortalPurchasev2 extends Component
$this->sub_total = Number::formatMoney($this->bundle->sum('total'), $this->subscription->company);
$this->total = $this->sub_total;
if($this->coupon == $this->subscription->promo_code)
if($this->valid_coupon)
{
if($this->subscription->is_amount_discount)
@ -443,6 +445,11 @@ class BillingPortalPurchasev2 extends Component
$this->float_amount_total = ($this->bundle->sum('total') - $discount);
}
else {
$this->float_amount_total = $this->bundle->sum('total');
$this->total = Number::formatMoney($this->float_amount_total, $this->subscription->company);
}
return $this;
@ -490,7 +497,6 @@ class BillingPortalPurchasev2 extends Component
*/
protected function getPaymentMethods(): self
{
$this->methods = $this->contact->client->service()->getPaymentMethods($this->float_amount_total);
return $this;
@ -505,6 +511,7 @@ class BillingPortalPurchasev2 extends Component
*/
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
{
$this->company_gateway_id = $company_gateway_id;
$this->payment_method_id = $gateway_type_id;
@ -546,19 +553,20 @@ class BillingPortalPurchasev2 extends Component
->service()
->createInvoiceV2($this->bundle, $this->contact->client_id, $this->valid_coupon)
->service()
// ->markSent()
->markSent()
->fillDefaults()
->adjustInventory()
->save();
// Cache::put($this->hash, [
// 'subscription_id' => $this->subscription->id,
// 'email' => $this->email ?? $this->contact->email,
// 'client_id' => $this->contact->client->id,
// 'invoice_id' => $this->invoice->id,
// 'context' => 'purchase',
// 'campaign' => $this->campaign,
// ], now()->addMinutes(60));
Cache::put($this->hash, [
'subscription_id' => $this->subscription->id,
'email' => $this->email ?? $this->contact->email,
'client_id' => $this->contact->client->id,
'invoice_id' => $this->invoice->id,
'context' => 'purchase',
'campaign' => $this->campaign,
'bundle' => $this->bundle,
], now()->addMinutes(60));
$this->emit('beforePaymentEventsCompleted');
}

View File

@ -112,10 +112,19 @@ class UpdateOrCreateProduct implements ShouldQueue
$product->tax_rate2 = isset($item->tax_rate2) ? $item->tax_rate2 : 0;
$product->tax_name3 = isset($item->tax_name3) ? $item->tax_name3 : '';
$product->tax_rate3 = isset($item->tax_rate3) ? $item->tax_rate3 : 0;
$product->custom_value1 = isset($item->custom_value1) ? $item->custom_value1 : '';
$product->custom_value2 = isset($item->custom_value2) ? $item->custom_value2 : '';
$product->custom_value3 = isset($item->custom_value3) ? $item->custom_value3 : '';
$product->custom_value4 = isset($item->custom_value4) ? $item->custom_value4 : '';
if(isset($item->custom_value1) && strlen($item->custom_value1) >=1)
$product->custom_value1 = $item->custom_value1;
if(isset($item->custom_value2) && strlen($item->custom_value1) >=1)
$product->custom_value2 = $item->custom_value2;
if(isset($item->custom_value3) && strlen($item->custom_value1) >=1)
$product->custom_value3 = $item->custom_value3;
if(isset($item->custom_value4) && strlen($item->custom_value1) >=1)
$product->custom_value4 = $item->custom_value4;
$product->user_id = $this->invoice->user_id;
$product->company_id = $this->invoice->company_id;
$product->project_id = $this->invoice->project_id;

View File

@ -118,6 +118,36 @@ class SubscriptionRepository extends BaseRepository
return $line_items;
}
public function generateBundleLineItems($bundle, $is_recurring = false, $is_credit = false)
{
$multiplier = $is_credit ? -1 : 1;
$line_items = [];
$line_items = collect($bundle)->filter(function ($item){
return $item->is_recurring;
})->map(function ($item){
$line_item = new InvoiceItem;
$line_item->product_key = $item->product_key;
$line_item->quantity = (float)$item->qty;
$line_item->cost = (float)$item->unit_cost;
$line_item->notes = $item->description;
return $line_item;
})->toArray();
$line_items = $this->cleanItems($line_items);
return $line_items;
}
private function makeLineItem($product, $multiplier)
{
$item = new InvoiceItem;

View File

@ -79,7 +79,11 @@ class SubscriptionService
// if we have a recurring product - then generate a recurring invoice
if(strlen($this->subscription->recurring_product_ids) >=1){
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash->payment->client_id);
if(isset($payment_hash->data->billing_context->bundle))
$recurring_invoice = $this->convertInvoiceToRecurringBundle($payment_hash->payment->client_id, $payment_hash->data->billing_context->bundle);
else
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash->payment->client_id);
$recurring_invoice_repo = new RecurringInvoiceRepository();
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
@ -718,18 +722,16 @@ class SubscriptionService
$line_item = new InvoiceItem;
$line_item->product_key = $item['product_key'];
$line_item->quantity = $item['qty'];
$line_item->cost = $item['unit_cost'];
$line_item->quantity = (float)$item['qty'];
$line_item->cost = (float)$item['unit_cost'];
$line_item->notes = $item['description'];
return $line_item;
})->toArray();
nlog($line_items);
$invoice->line_items = $line_items;
if($valid_coupon){
$invoice->discount = $this->subscription->promo_discount;
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
@ -803,6 +805,41 @@ nlog($line_items);
return $recurring_invoice;
}
/**
* Generates a recurring invoice based on
* the specifications of the subscription USING BUNDLE
*
* @param int $client_id The Client Id
* @return RecurringInvoice
*/
public function convertInvoiceToRecurringBundle($client_id, $bundle) :RecurringInvoice
{
MultiDB::setDb($this->subscription->company->db);
$client = Client::withTrashed()->find($client_id);
$subscription_repo = new SubscriptionRepository();
$recurring_invoice = RecurringInvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
$recurring_invoice->client_id = $client_id;
$recurring_invoice->line_items = $subscription_repo->generateBundleLineItems($bundle, true, false);
$recurring_invoice->subscription_id = $this->subscription->id;
$recurring_invoice->frequency_id = $this->subscription->frequency_id ?: RecurringInvoice::FREQUENCY_MONTHLY;
$recurring_invoice->date = now();
$recurring_invoice->remaining_cycles = -1;
$recurring_invoice->auto_bill = $client->getSetting('auto_bill');
$recurring_invoice->auto_bill_enabled = $this->setAutoBillFlag($recurring_invoice->auto_bill);
$recurring_invoice->due_date_days = 'terms';
$recurring_invoice->next_send_date = now()->format('Y-m-d');
$recurring_invoice->next_send_date_client = now()->format('Y-m-d');
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
$recurring_invoice->next_send_date_client = $recurring_invoice->nextSendDateClient();
return $recurring_invoice;
}
private function setAutoBillFlag($auto_bill)
{
if ($auto_bill == 'always' || $auto_bill == 'optout') {

View File

@ -4902,7 +4902,11 @@ $LANG = array(
'delete_tax_rate' => 'Delete Tax Rate',
'restore_tax_rate' => 'Restore Tax Rate',
'company_backup_file' => 'Select company backup file',
'company_backup_file_help' => 'Please upload the .zip file used to create this backup.'
'company_backup_file_help' => 'Please upload the .zip file used to create this backup.',
'backup_restore' => 'Backup | Restore',
'export_company' => 'Create company backup',
'backup' => 'Backup',
);
return $LANG;

View File

@ -25,8 +25,8 @@
@endif
<input type="hidden" name="action" value="payment">
<input type="hidden" name="company_gateway_id" value=""/>
<input type="hidden" name="payment_method_id" value=""/>
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}"/>
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"/>
</form>
</div>
@endif
@ -261,7 +261,7 @@
{{ session('message') }}
@endcomponent
@endif
@if(count($methods) > 0 && !$payment_started)
@if(count($methods) > 0)
<div class="mt-4">
@foreach($methods as $method)
<button