Merge pull request #8076 from turbo124/v5-develop

Subscriptions v2
This commit is contained in:
David Bomba 2022-12-15 14:41:36 +11:00 committed by GitHub
commit b020b627ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 148 additions and 65 deletions

View File

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

View File

@ -130,7 +130,7 @@ class InvoiceEmailEngine extends BaseEmailEngine
$pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle()); $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
$this->setAttachments([['file' => base64_encode($pdf), 'name' => ctrans('texts.invoice') . " " .$this->invoice->numberFormatter().'.pdf']]); $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->invoice->numberFormatter().'.pdf']]);
} }
//attach third party documents //attach third party documents
@ -138,11 +138,11 @@ class InvoiceEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->invoice->documents as $document) { foreach ($this->invoice->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
foreach ($this->invoice->company->documents as $document) { foreach ($this->invoice->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
$line_items = $this->invoice->line_items; $line_items = $this->invoice->line_items;

View File

@ -117,11 +117,6 @@ class QuoteEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
// if (Ninja::isHosted()) {
// $this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]);
// } else {
// $this->setAttachments([$this->quote->pdf_file_path($this->invitation)]);
// }
$pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle()); $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
@ -133,11 +128,11 @@ class QuoteEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->quote->documents as $document) { foreach ($this->quote->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
foreach ($this->quote->company->documents as $document) { foreach ($this->quote->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
} }

View File

@ -118,6 +118,36 @@ class SubscriptionRepository extends BaseRepository
return $line_items; 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) private function makeLineItem($product, $multiplier)
{ {
$item = new InvoiceItem; $item = new InvoiceItem;

View File

@ -79,7 +79,11 @@ class SubscriptionService
// if we have a recurring product - then generate a recurring invoice // 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){
$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_repo = new RecurringInvoiceRepository();
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice); $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
@ -718,18 +722,16 @@ class SubscriptionService
$line_item = new InvoiceItem; $line_item = new InvoiceItem;
$line_item->product_key = $item['product_key']; $line_item->product_key = $item['product_key'];
$line_item->quantity = $item['qty']; $line_item->quantity = (float)$item['qty'];
$line_item->cost = $item['unit_cost']; $line_item->cost = (float)$item['unit_cost'];
$line_item->notes = $item['description']; $line_item->notes = $item['description'];
return $line_item; return $line_item;
})->toArray(); })->toArray();
nlog($line_items);
$invoice->line_items = $line_items; $invoice->line_items = $line_items;
if($valid_coupon){ if($valid_coupon){
$invoice->discount = $this->subscription->promo_discount; $invoice->discount = $this->subscription->promo_discount;
$invoice->is_amount_discount = $this->subscription->is_amount_discount; $invoice->is_amount_discount = $this->subscription->is_amount_discount;
@ -803,6 +805,41 @@ nlog($line_items);
return $recurring_invoice; 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) private function setAutoBillFlag($auto_bill)
{ {
if ($auto_bill == 'always' || $auto_bill == 'optout') { if ($auto_bill == 'always' || $auto_bill == 'optout') {

View File

@ -4902,7 +4902,11 @@ $LANG = array(
'delete_tax_rate' => 'Delete Tax Rate', 'delete_tax_rate' => 'Delete Tax Rate',
'restore_tax_rate' => 'Restore Tax Rate', 'restore_tax_rate' => 'Restore Tax Rate',
'company_backup_file' => 'Select company backup file', '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; return $LANG;

View File

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