Merge pull request #315 from denisdulici/recurring
Recurring for invoice, revenue, bill and payment
This commit is contained in:
commit
72df6ac8f9
186
app/Console/Commands/RecurringCheck.php
Normal file
186
app/Console/Commands/RecurringCheck.php
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Company\Company;
|
||||||
|
use App\Models\Common\Recurring;
|
||||||
|
use App\Models\Expense\Bill;
|
||||||
|
use App\Models\Expense\BillHistory;
|
||||||
|
use App\Models\Expense\Payment;
|
||||||
|
use App\Models\Income\Invoice;
|
||||||
|
use App\Models\Income\InvoiceHistory;
|
||||||
|
use App\Models\Income\Revenue;
|
||||||
|
use App\Notifications\Expense\Bill as BillNotification;
|
||||||
|
use App\Notifications\Income\Invoice as InvoiceNotification;
|
||||||
|
use App\Traits\Incomes;
|
||||||
|
use App\Utilities\Overrider;
|
||||||
|
use Date;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Recurr\Rule;
|
||||||
|
use Recurr\Transformer\ArrayTransformer;
|
||||||
|
use Recurr\Transformer\ArrayTransformerConfig;
|
||||||
|
|
||||||
|
class RecurringCheck extends Command
|
||||||
|
{
|
||||||
|
use Incomes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'recurring:check';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Check for recurring';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$today = Date::today();
|
||||||
|
|
||||||
|
// Get all companies
|
||||||
|
$companies = Company::all();
|
||||||
|
|
||||||
|
foreach ($companies as $company) {
|
||||||
|
// Set company id
|
||||||
|
session(['company_id' => $company->id]);
|
||||||
|
|
||||||
|
// Override settings and currencies
|
||||||
|
Overrider::load('settings');
|
||||||
|
Overrider::load('currencies');
|
||||||
|
|
||||||
|
$company->setSettings();
|
||||||
|
|
||||||
|
$recurring = $company->recurring();
|
||||||
|
|
||||||
|
foreach ($recurring as $recur) {
|
||||||
|
$schedule = $this->schedule($recur);
|
||||||
|
|
||||||
|
$current = Date::parse($schedule->current()->getStart());
|
||||||
|
|
||||||
|
// Check if should recur today
|
||||||
|
if ($today->ne($current)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = end(explode('\\', $recur->recurable_type));
|
||||||
|
|
||||||
|
$model = $type::find($recur->recurable_id);
|
||||||
|
|
||||||
|
if (!$model) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'Bill':
|
||||||
|
$this->recurBill($company, $model);
|
||||||
|
break;
|
||||||
|
case 'Invoice':
|
||||||
|
$this->recurInvoice($company, $model);
|
||||||
|
break;
|
||||||
|
case 'Payment':
|
||||||
|
case 'Revenue':
|
||||||
|
$model->duplicate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset company_id
|
||||||
|
session()->forget('company_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recurInvoice($company, $model)
|
||||||
|
{
|
||||||
|
$clone = $model->duplicate();
|
||||||
|
|
||||||
|
// Add invoice history
|
||||||
|
InvoiceHistory::create([
|
||||||
|
'company_id' => session('company_id'),
|
||||||
|
'invoice_id' => $clone->id,
|
||||||
|
'status_code' => 'draft',
|
||||||
|
'notify' => 0,
|
||||||
|
'description' => trans('messages.success.added', ['type' => $clone->invoice_number]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Notify the customer
|
||||||
|
if ($clone->customer && !empty($clone->customer_email)) {
|
||||||
|
$clone->customer->notify(new InvoiceNotification($clone));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all users assigned to this company
|
||||||
|
foreach ($company->users as $user) {
|
||||||
|
if (!$user->can('read-notifications')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->notify(new InvoiceNotification($clone));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update next invoice number
|
||||||
|
$this->increaseNextInvoiceNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recurBill($company, $model)
|
||||||
|
{
|
||||||
|
$clone = $model->duplicate();
|
||||||
|
|
||||||
|
// Add bill history
|
||||||
|
BillHistory::create([
|
||||||
|
'company_id' => session('company_id'),
|
||||||
|
'bill_id' => $clone->id,
|
||||||
|
'status_code' => 'draft',
|
||||||
|
'notify' => 0,
|
||||||
|
'description' => trans('messages.success.added', ['type' => $clone->bill_number]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Notify all users assigned to this company
|
||||||
|
foreach ($company->users as $user) {
|
||||||
|
if (!$user->can('read-notifications')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->notify(new BillNotification($clone));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function schedule($recur)
|
||||||
|
{
|
||||||
|
$config = new ArrayTransformerConfig();
|
||||||
|
$config->enableLastDayOfMonthFix();
|
||||||
|
|
||||||
|
$transformer = new ArrayTransformer();
|
||||||
|
$transformer->setConfig($config);
|
||||||
|
|
||||||
|
return $transformer->transform($this->rule($recur));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function rule($recur)
|
||||||
|
{
|
||||||
|
$rule = (new Rule())
|
||||||
|
->setStartDate($recur->started_at)
|
||||||
|
->setTimezone(setting('general.timezone'))
|
||||||
|
->setFreq(strtoupper($recur->frequency))
|
||||||
|
->setInterval($recur->interval)
|
||||||
|
->setCount($recur->count);
|
||||||
|
|
||||||
|
return $rule;
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ class Kernel extends ConsoleKernel
|
|||||||
Commands\Install::class,
|
Commands\Install::class,
|
||||||
Commands\InvoiceReminder::class,
|
Commands\InvoiceReminder::class,
|
||||||
Commands\ModuleInstall::class,
|
Commands\ModuleInstall::class,
|
||||||
|
Commands\RecurringCheck::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,6 +242,9 @@ class Bills extends Controller
|
|||||||
'description' => trans('messages.success.added', ['type' => $bill->bill_number]),
|
'description' => trans('messages.success.added', ['type' => $bill->bill_number]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$bill->createRecurring();
|
||||||
|
|
||||||
// Fire the event to make it extendible
|
// Fire the event to make it extendible
|
||||||
event(new BillCreated($bill));
|
event(new BillCreated($bill));
|
||||||
|
|
||||||
@ -444,6 +447,9 @@ class Bills extends Controller
|
|||||||
$bill->totals()->delete();
|
$bill->totals()->delete();
|
||||||
$this->addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
$this->addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$bill->updateRecurring();
|
||||||
|
|
||||||
// Fire the event to make it extendible
|
// Fire the event to make it extendible
|
||||||
event(new BillUpdated($bill));
|
event(new BillUpdated($bill));
|
||||||
|
|
||||||
@ -511,9 +517,7 @@ class Bills extends Controller
|
|||||||
{
|
{
|
||||||
$bill = $this->prepareBill($bill);
|
$bill = $this->prepareBill($bill);
|
||||||
|
|
||||||
$logo = $this->getLogo($bill);
|
return view($bill->template_path, compact('bill'));
|
||||||
|
|
||||||
return view($bill->template_path, compact('bill', 'logo'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -527,9 +531,7 @@ class Bills extends Controller
|
|||||||
{
|
{
|
||||||
$bill = $this->prepareBill($bill);
|
$bill = $this->prepareBill($bill);
|
||||||
|
|
||||||
$logo = $this->getLogo($bill);
|
$html = view($bill->template_path, compact('bill'))->render();
|
||||||
|
|
||||||
$html = view($bill->template_path, compact('bill', 'logo'))->render();
|
|
||||||
|
|
||||||
$pdf = \App::make('dompdf.wrapper');
|
$pdf = \App::make('dompdf.wrapper');
|
||||||
$pdf->loadHTML($html);
|
$pdf->loadHTML($html);
|
||||||
@ -736,39 +738,4 @@ class Bills extends Controller
|
|||||||
'sort_order' => $sort_order,
|
'sort_order' => $sort_order,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLogo($bill)
|
|
||||||
{
|
|
||||||
$logo = '';
|
|
||||||
|
|
||||||
$media_id = setting('general.company_logo');
|
|
||||||
|
|
||||||
if (isset($bill->vendor->logo) && !empty($bill->vendor->logo->id)) {
|
|
||||||
$media_id = $bill->vendor->logo->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$media = Media::find($media_id);
|
|
||||||
|
|
||||||
if (!empty($media)) {
|
|
||||||
$path = Storage::path($media->getDiskPath());
|
|
||||||
|
|
||||||
if (!is_file($path)) {
|
|
||||||
return $logo;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$path = asset('public/img/company.png');
|
|
||||||
}
|
|
||||||
|
|
||||||
$image = Image::make($path)->encode()->getEncoded();
|
|
||||||
|
|
||||||
if (empty($image)) {
|
|
||||||
return $logo;
|
|
||||||
}
|
|
||||||
|
|
||||||
$extension = File::extension($path);
|
|
||||||
|
|
||||||
$logo = 'data:image/' . $extension . ';base64,' . base64_encode($image);
|
|
||||||
|
|
||||||
return $logo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,9 @@ class Payments extends Controller
|
|||||||
$payment->attachMedia($media, 'attachment');
|
$payment->attachMedia($media, 'attachment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$payment->createRecurring();
|
||||||
|
|
||||||
$message = trans('messages.success.added', ['type' => trans_choice('general.payments', 1)]);
|
$message = trans('messages.success.added', ['type' => trans_choice('general.payments', 1)]);
|
||||||
|
|
||||||
flash($message)->success();
|
flash($message)->success();
|
||||||
@ -195,6 +198,9 @@ class Payments extends Controller
|
|||||||
$payment->attachMedia($media, 'attachment');
|
$payment->attachMedia($media, 'attachment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$payment->updateRecurring();
|
||||||
|
|
||||||
$message = trans('messages.success.updated', ['type' => trans_choice('general.payments', 1)]);
|
$message = trans('messages.success.updated', ['type' => trans_choice('general.payments', 1)]);
|
||||||
|
|
||||||
flash($message)->success();
|
flash($message)->success();
|
||||||
|
@ -260,6 +260,9 @@ class Invoices extends Controller
|
|||||||
// Update next invoice number
|
// Update next invoice number
|
||||||
$this->increaseNextInvoiceNumber();
|
$this->increaseNextInvoiceNumber();
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$invoice->createRecurring();
|
||||||
|
|
||||||
// Fire the event to make it extendible
|
// Fire the event to make it extendible
|
||||||
event(new InvoiceCreated($invoice));
|
event(new InvoiceCreated($invoice));
|
||||||
|
|
||||||
@ -465,6 +468,9 @@ class Invoices extends Controller
|
|||||||
$invoice->totals()->delete();
|
$invoice->totals()->delete();
|
||||||
$this->addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
$this->addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$invoice->updateRecurring();
|
||||||
|
|
||||||
// Fire the event to make it extendible
|
// Fire the event to make it extendible
|
||||||
event(new InvoiceUpdated($invoice));
|
event(new InvoiceUpdated($invoice));
|
||||||
|
|
||||||
@ -546,9 +552,7 @@ class Invoices extends Controller
|
|||||||
|
|
||||||
$invoice = $this->prepareInvoice($invoice);
|
$invoice = $this->prepareInvoice($invoice);
|
||||||
|
|
||||||
$logo = $this->getLogo();
|
$html = view($invoice->template_path, compact('invoice'))->render();
|
||||||
|
|
||||||
$html = view($invoice->template_path, compact('invoice', 'logo'))->render();
|
|
||||||
|
|
||||||
$pdf = \App::make('dompdf.wrapper');
|
$pdf = \App::make('dompdf.wrapper');
|
||||||
$pdf->loadHTML($html);
|
$pdf->loadHTML($html);
|
||||||
@ -602,9 +606,7 @@ class Invoices extends Controller
|
|||||||
{
|
{
|
||||||
$invoice = $this->prepareInvoice($invoice);
|
$invoice = $this->prepareInvoice($invoice);
|
||||||
|
|
||||||
$logo = $this->getLogo();
|
return view($invoice->template_path, compact('invoice'));
|
||||||
|
|
||||||
return view($invoice->template_path, compact('invoice', 'logo'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -618,9 +620,7 @@ class Invoices extends Controller
|
|||||||
{
|
{
|
||||||
$invoice = $this->prepareInvoice($invoice);
|
$invoice = $this->prepareInvoice($invoice);
|
||||||
|
|
||||||
$logo = $this->getLogo();
|
$html = view($invoice->template_path, compact('invoice'))->render();
|
||||||
|
|
||||||
$html = view($invoice->template_path, compact('invoice', 'logo'))->render();
|
|
||||||
|
|
||||||
$pdf = \App::make('dompdf.wrapper');
|
$pdf = \App::make('dompdf.wrapper');
|
||||||
$pdf->loadHTML($html);
|
$pdf->loadHTML($html);
|
||||||
@ -869,39 +869,4 @@ class Invoices extends Controller
|
|||||||
'sort_order' => $sort_order,
|
'sort_order' => $sort_order,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLogo()
|
|
||||||
{
|
|
||||||
$logo = '';
|
|
||||||
|
|
||||||
$media_id = setting('general.company_logo');
|
|
||||||
|
|
||||||
if (setting('general.invoice_logo')) {
|
|
||||||
$media_id = setting('general.invoice_logo');
|
|
||||||
}
|
|
||||||
|
|
||||||
$media = Media::find($media_id);
|
|
||||||
|
|
||||||
if (!empty($media)) {
|
|
||||||
$path = Storage::path($media->getDiskPath());
|
|
||||||
|
|
||||||
if (!is_file($path)) {
|
|
||||||
return $logo;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$path = asset('public/img/company.png');
|
|
||||||
}
|
|
||||||
|
|
||||||
$image = Image::make($path)->encode()->getEncoded();
|
|
||||||
|
|
||||||
if (empty($image)) {
|
|
||||||
return $logo;
|
|
||||||
}
|
|
||||||
|
|
||||||
$extension = File::extension($path);
|
|
||||||
|
|
||||||
$logo = 'data:image/' . $extension . ';base64,' . base64_encode($image);
|
|
||||||
|
|
||||||
return $logo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,9 @@ class Revenues extends Controller
|
|||||||
$revenue->attachMedia($media, 'attachment');
|
$revenue->attachMedia($media, 'attachment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$revenue->createRecurring();
|
||||||
|
|
||||||
$message = trans('messages.success.added', ['type' => trans_choice('general.revenues', 1)]);
|
$message = trans('messages.success.added', ['type' => trans_choice('general.revenues', 1)]);
|
||||||
|
|
||||||
flash($message)->success();
|
flash($message)->success();
|
||||||
@ -197,6 +200,9 @@ class Revenues extends Controller
|
|||||||
$revenue->attachMedia($media, 'attachment');
|
$revenue->attachMedia($media, 'attachment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
$revenue->updateRecurring();
|
||||||
|
|
||||||
$message = trans('messages.success.updated', ['type' => trans_choice('general.revenues', 1)]);
|
$message = trans('messages.success.updated', ['type' => trans_choice('general.revenues', 1)]);
|
||||||
|
|
||||||
flash($message)->success();
|
flash($message)->success();
|
||||||
|
54
app/Http/ViewComposers/Logo.php
Normal file
54
app/Http/ViewComposers/Logo.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\ViewComposers;
|
||||||
|
|
||||||
|
use App\Models\Common\Media;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use File;
|
||||||
|
use Image;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
|
class Logo
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind data to the view.
|
||||||
|
*
|
||||||
|
* @param View $view
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function compose(View $view)
|
||||||
|
{
|
||||||
|
$logo = '';
|
||||||
|
|
||||||
|
$media_id = setting('general.company_logo');
|
||||||
|
|
||||||
|
if (setting('general.invoice_logo')) {
|
||||||
|
$media_id = setting('general.invoice_logo');
|
||||||
|
}
|
||||||
|
|
||||||
|
$media = Media::find($media_id);
|
||||||
|
|
||||||
|
if (!empty($media)) {
|
||||||
|
$path = Storage::path($media->getDiskPath());
|
||||||
|
|
||||||
|
if (!is_file($path)) {
|
||||||
|
return $logo;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$path = asset('public/img/company.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = Image::make($path)->encode()->getEncoded();
|
||||||
|
|
||||||
|
if (empty($image)) {
|
||||||
|
return $logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
$extension = File::extension($path);
|
||||||
|
|
||||||
|
$logo = 'data:image/' . $extension . ';base64,' . base64_encode($image);
|
||||||
|
|
||||||
|
$view->with(['logo' => $logo]);
|
||||||
|
}
|
||||||
|
}
|
36
app/Http/ViewComposers/Recurring.php
Normal file
36
app/Http/ViewComposers/Recurring.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\ViewComposers;
|
||||||
|
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
class Recurring
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind data to the view.
|
||||||
|
*
|
||||||
|
* @param View $view
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function compose(View $view)
|
||||||
|
{
|
||||||
|
$recurring_frequencies = [
|
||||||
|
'no' => trans('general.no'),
|
||||||
|
'daily' => trans('recurring.daily'),
|
||||||
|
'weekly' => trans('recurring.weekly'),
|
||||||
|
'monthly' => trans('recurring.monthly'),
|
||||||
|
'yearly' => trans('recurring.yearly'),
|
||||||
|
'custom' => trans('recurring.custom'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$recurring_custom_frequencies = [
|
||||||
|
'daily' => trans('recurring.days'),
|
||||||
|
'weekly' => trans('recurring.weeks'),
|
||||||
|
'monthly' => trans('recurring.months'),
|
||||||
|
'yearly' => trans('recurring.years'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$view->with(['recurring_frequencies' => $recurring_frequencies, 'recurring_custom_frequencies' => $recurring_custom_frequencies]);
|
||||||
|
}
|
||||||
|
}
|
27
app/Models/Common/Recurring.php
Normal file
27
app/Models/Common/Recurring.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Common;
|
||||||
|
|
||||||
|
use App\Models\Model;
|
||||||
|
|
||||||
|
class Recurring extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $table = 'recurring';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes that should be mass-assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = ['company_id', 'recurable_id', 'recurable_type', 'frequency', 'interval', 'started_at', 'count'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all of the owning recurrable models.
|
||||||
|
*/
|
||||||
|
public function recurable()
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
|
}
|
@ -106,6 +106,11 @@ class Company extends Eloquent
|
|||||||
return $this->hasMany('App\Models\Expense\Payment');
|
return $this->hasMany('App\Models\Expense\Payment');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function recurring()
|
||||||
|
{
|
||||||
|
return $this->hasMany('App\Models\Common\Recurring');
|
||||||
|
}
|
||||||
|
|
||||||
public function revenues()
|
public function revenues()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Models\Income\Revenue');
|
return $this->hasMany('App\Models\Income\Revenue');
|
||||||
|
@ -5,13 +5,14 @@ namespace App\Models\Expense;
|
|||||||
use App\Models\Model;
|
use App\Models\Model;
|
||||||
use App\Traits\Currencies;
|
use App\Traits\Currencies;
|
||||||
use App\Traits\DateTime;
|
use App\Traits\DateTime;
|
||||||
|
use App\Traits\Media;
|
||||||
|
use App\Traits\Recurring;
|
||||||
use Bkwld\Cloner\Cloneable;
|
use Bkwld\Cloner\Cloneable;
|
||||||
use Sofa\Eloquence\Eloquence;
|
use Sofa\Eloquence\Eloquence;
|
||||||
use App\Traits\Media;
|
|
||||||
|
|
||||||
class Bill extends Model
|
class Bill extends Model
|
||||||
{
|
{
|
||||||
use Cloneable, Currencies, DateTime, Eloquence, Media;
|
use Cloneable, Currencies, DateTime, Eloquence, Media, Recurring;
|
||||||
|
|
||||||
protected $table = 'bills';
|
protected $table = 'bills';
|
||||||
|
|
||||||
@ -58,26 +59,21 @@ class Bill extends Model
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $cloneable_relations = ['items', 'totals'];
|
protected $cloneable_relations = ['items', 'recurring', 'totals'];
|
||||||
|
|
||||||
public function category()
|
public function category()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Setting\Category');
|
return $this->belongsTo('App\Models\Setting\Category');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function vendor()
|
|
||||||
{
|
|
||||||
return $this->belongsTo('App\Models\Expense\Vendor');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function currency()
|
public function currency()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status()
|
public function histories()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Expense\BillStatus', 'bill_status_code', 'code');
|
return $this->hasMany('App\Models\Expense\BillHistory');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function items()
|
public function items()
|
||||||
@ -85,19 +81,29 @@ class Bill extends Model
|
|||||||
return $this->hasMany('App\Models\Expense\BillItem');
|
return $this->hasMany('App\Models\Expense\BillItem');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function totals()
|
|
||||||
{
|
|
||||||
return $this->hasMany('App\Models\Expense\BillTotal');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function payments()
|
public function payments()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Models\Expense\BillPayment');
|
return $this->hasMany('App\Models\Expense\BillPayment');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function histories()
|
public function recurring()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Models\Expense\BillHistory');
|
return $this->morphOne('App\Models\Common\Recurring', 'recurable');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Expense\BillStatus', 'bill_status_code', 'code');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totals()
|
||||||
|
{
|
||||||
|
return $this->hasMany('App\Models\Expense\BillTotal');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function vendor()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Expense\Vendor');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeDue($query, $date)
|
public function scopeDue($query, $date)
|
||||||
|
@ -6,13 +6,14 @@ use App\Models\Model;
|
|||||||
use App\Models\Setting\Category;
|
use App\Models\Setting\Category;
|
||||||
use App\Traits\Currencies;
|
use App\Traits\Currencies;
|
||||||
use App\Traits\DateTime;
|
use App\Traits\DateTime;
|
||||||
|
use App\Traits\Media;
|
||||||
|
use App\Traits\Recurring;
|
||||||
use Bkwld\Cloner\Cloneable;
|
use Bkwld\Cloner\Cloneable;
|
||||||
use Sofa\Eloquence\Eloquence;
|
use Sofa\Eloquence\Eloquence;
|
||||||
use App\Traits\Media;
|
|
||||||
|
|
||||||
class Payment extends Model
|
class Payment extends Model
|
||||||
{
|
{
|
||||||
use Cloneable, Currencies, DateTime, Eloquence, Media;
|
use Cloneable, Currencies, DateTime, Eloquence, Media, Recurring;
|
||||||
|
|
||||||
protected $table = 'payments';
|
protected $table = 'payments';
|
||||||
|
|
||||||
@ -44,24 +45,31 @@ class Payment extends Model
|
|||||||
'description' ,
|
'description' ,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clonable relationships.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $cloneable_relations = ['recurring'];
|
||||||
|
|
||||||
public function account()
|
public function account()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Banking\Account');
|
return $this->belongsTo('App\Models\Banking\Account');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function currency()
|
|
||||||
{
|
|
||||||
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function category()
|
public function category()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Setting\Category');
|
return $this->belongsTo('App\Models\Setting\Category');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function vendor()
|
public function currency()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Expense\Vendor');
|
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recurring()
|
||||||
|
{
|
||||||
|
return $this->morphOne('App\Models\Common\Recurring', 'recurable');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function transfers()
|
public function transfers()
|
||||||
@ -69,6 +77,11 @@ class Payment extends Model
|
|||||||
return $this->hasMany('App\Models\Banking\Transfer');
|
return $this->hasMany('App\Models\Banking\Transfer');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function vendor()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Expense\Vendor');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get only transfers.
|
* Get only transfers.
|
||||||
*
|
*
|
||||||
|
@ -6,13 +6,14 @@ use App\Models\Model;
|
|||||||
use App\Traits\Currencies;
|
use App\Traits\Currencies;
|
||||||
use App\Traits\DateTime;
|
use App\Traits\DateTime;
|
||||||
use App\Traits\Incomes;
|
use App\Traits\Incomes;
|
||||||
|
use App\Traits\Media;
|
||||||
|
use App\Traits\Recurring;
|
||||||
use Bkwld\Cloner\Cloneable;
|
use Bkwld\Cloner\Cloneable;
|
||||||
use Sofa\Eloquence\Eloquence;
|
use Sofa\Eloquence\Eloquence;
|
||||||
use App\Traits\Media;
|
|
||||||
|
|
||||||
class Invoice extends Model
|
class Invoice extends Model
|
||||||
{
|
{
|
||||||
use Cloneable, Currencies, DateTime, Eloquence, Incomes, Media;
|
use Cloneable, Currencies, DateTime, Eloquence, Incomes, Media, Recurring;
|
||||||
|
|
||||||
protected $table = 'invoices';
|
protected $table = 'invoices';
|
||||||
|
|
||||||
@ -59,26 +60,21 @@ class Invoice extends Model
|
|||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $cloneable_relations = ['items', 'totals'];
|
protected $cloneable_relations = ['items', 'recurring', 'totals'];
|
||||||
|
|
||||||
public function category()
|
public function category()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Setting\Category');
|
return $this->belongsTo('App\Models\Setting\Category');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function customer()
|
|
||||||
{
|
|
||||||
return $this->belongsTo('App\Models\Income\Customer');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function currency()
|
public function currency()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status()
|
public function customer()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Income\InvoiceStatus', 'invoice_status_code', 'code');
|
return $this->belongsTo('App\Models\Income\Customer');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function items()
|
public function items()
|
||||||
@ -86,9 +82,9 @@ class Invoice extends Model
|
|||||||
return $this->hasMany('App\Models\Income\InvoiceItem');
|
return $this->hasMany('App\Models\Income\InvoiceItem');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function totals()
|
public function histories()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Models\Income\InvoiceTotal');
|
return $this->hasMany('App\Models\Income\InvoiceHistory');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function payments()
|
public function payments()
|
||||||
@ -96,9 +92,19 @@ class Invoice extends Model
|
|||||||
return $this->hasMany('App\Models\Income\InvoicePayment');
|
return $this->hasMany('App\Models\Income\InvoicePayment');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function histories()
|
public function recurring()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Models\Income\InvoiceHistory');
|
return $this->morphOne('App\Models\Common\Recurring', 'recurable');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Income\InvoiceStatus', 'invoice_status_code', 'code');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function totals()
|
||||||
|
{
|
||||||
|
return $this->hasMany('App\Models\Income\InvoiceTotal');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeDue($query, $date)
|
public function scopeDue($query, $date)
|
||||||
|
@ -6,13 +6,14 @@ use App\Models\Model;
|
|||||||
use App\Models\Setting\Category;
|
use App\Models\Setting\Category;
|
||||||
use App\Traits\Currencies;
|
use App\Traits\Currencies;
|
||||||
use App\Traits\DateTime;
|
use App\Traits\DateTime;
|
||||||
|
use App\Traits\Media;
|
||||||
|
use App\Traits\Recurring;
|
||||||
use Bkwld\Cloner\Cloneable;
|
use Bkwld\Cloner\Cloneable;
|
||||||
use Sofa\Eloquence\Eloquence;
|
use Sofa\Eloquence\Eloquence;
|
||||||
use App\Traits\Media;
|
|
||||||
|
|
||||||
class Revenue extends Model
|
class Revenue extends Model
|
||||||
{
|
{
|
||||||
use Cloneable, Currencies, DateTime, Eloquence, Media;
|
use Cloneable, Currencies, DateTime, Eloquence, Media, Recurring;
|
||||||
|
|
||||||
protected $table = 'revenues';
|
protected $table = 'revenues';
|
||||||
|
|
||||||
@ -45,6 +46,13 @@ class Revenue extends Model
|
|||||||
'notes' => 2,
|
'notes' => 2,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clonable relationships.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $cloneable_relations = ['recurring'];
|
||||||
|
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Auth\User', 'customer_id', 'id');
|
return $this->belongsTo('App\Models\Auth\User', 'customer_id', 'id');
|
||||||
@ -70,6 +78,11 @@ class Revenue extends Model
|
|||||||
return $this->belongsTo('App\Models\Income\Customer');
|
return $this->belongsTo('App\Models\Income\Customer');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function recurring()
|
||||||
|
{
|
||||||
|
return $this->morphOne('App\Models\Common\Recurring', 'recurable');
|
||||||
|
}
|
||||||
|
|
||||||
public function transfers()
|
public function transfers()
|
||||||
{
|
{
|
||||||
return $this->hasMany('App\Models\Banking\Transfer');
|
return $this->hasMany('App\Models\Banking\Transfer');
|
||||||
|
@ -62,6 +62,10 @@ class FormServiceProvider extends ServiceProvider
|
|||||||
Form::component('saveButtons', 'partials.form.save_buttons', [
|
Form::component('saveButtons', 'partials.form.save_buttons', [
|
||||||
'cancel', 'col' => 'col-md-12',
|
'cancel', 'col' => 'col-md-12',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Form::component('recurring', 'partials.form.recurring', [
|
||||||
|
'page', 'model' => null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +38,16 @@ class ViewComposerServiceProvider extends ServiceProvider
|
|||||||
View::composer(
|
View::composer(
|
||||||
'modules.*', 'App\Http\ViewComposers\Modules'
|
'modules.*', 'App\Http\ViewComposers\Modules'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Add recurring
|
||||||
|
View::composer(
|
||||||
|
['partials.form.recurring',], 'App\Http\ViewComposers\Recurring'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add logo
|
||||||
|
View::composer(
|
||||||
|
['incomes.invoices.invoice', 'expenses.bills.bill'], 'App\Http\ViewComposers\Logo'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
58
app/Traits/Recurring.php
Normal file
58
app/Traits/Recurring.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
trait Recurring
|
||||||
|
{
|
||||||
|
|
||||||
|
public function createRecurring()
|
||||||
|
{
|
||||||
|
$request = request();
|
||||||
|
|
||||||
|
if ($request->get('recurring_frequency') == 'no') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$frequency = ($request['recurring_frequency'] != 'custom') ? $request['recurring_frequency'] : $request['recurring_custom_frequency'];
|
||||||
|
$interval = ($request['recurring_frequency'] != 'custom') ? 1 : (int) $request['recurring_interval'];
|
||||||
|
$started_at = $request->get('paid_at') ?: ($request->get('invoiced_at') ?: $request->get('billed_at'));
|
||||||
|
|
||||||
|
$this->recurring()->create([
|
||||||
|
'company_id' => session('company_id'),
|
||||||
|
'frequency' => $frequency,
|
||||||
|
'interval' => $interval,
|
||||||
|
'started_at' => $started_at,
|
||||||
|
'count' => (int) $request['recurring_count'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateRecurring()
|
||||||
|
{
|
||||||
|
$request = request();
|
||||||
|
|
||||||
|
if ($request->get('recurring_frequency') == 'no') {
|
||||||
|
$this->recurring()->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$frequency = ($request['recurring_frequency'] != 'custom') ? $request['recurring_frequency'] : $request['recurring_custom_frequency'];
|
||||||
|
$interval = ($request['recurring_frequency'] != 'custom') ? 1 : (int) $request['recurring_interval'];
|
||||||
|
$started_at = $request->get('paid_at') ?: ($request->get('invoiced_at') ?: $request->get('billed_at'));
|
||||||
|
|
||||||
|
$recurring = $this->recurring();
|
||||||
|
|
||||||
|
if ($recurring->count()) {
|
||||||
|
$function = 'update';
|
||||||
|
} else {
|
||||||
|
$function = 'create';
|
||||||
|
}
|
||||||
|
|
||||||
|
$recurring->$function([
|
||||||
|
'company_id' => session('company_id'),
|
||||||
|
'frequency' => $frequency,
|
||||||
|
'interval' => $interval,
|
||||||
|
'started_at' => $started_at,
|
||||||
|
'count' => (int) $request['recurring_count'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@
|
|||||||
"nwidart/laravel-modules": "1.*",
|
"nwidart/laravel-modules": "1.*",
|
||||||
"plank/laravel-mediable": "2.5.*",
|
"plank/laravel-mediable": "2.5.*",
|
||||||
"santigarcor/laratrust": "4.0.*",
|
"santigarcor/laratrust": "4.0.*",
|
||||||
|
"simshaun/recurr": "3.0.*",
|
||||||
"sofa/eloquence": "5.4.*",
|
"sofa/eloquence": "5.4.*",
|
||||||
"tucker-eric/eloquentfilter": "1.1.*"
|
"tucker-eric/eloquentfilter": "1.1.*"
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
class CreateRecurringTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('recurring', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('company_id');
|
||||||
|
$table->morphs('recurable');
|
||||||
|
$table->string('frequency');
|
||||||
|
$table->integer('interval')->default(1);
|
||||||
|
$table->date('started_at');
|
||||||
|
$table->integer('count')->default(0);
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::drop('recurring');
|
||||||
|
}
|
||||||
|
}
|
5
public/css/app.css
vendored
5
public/css/app.css
vendored
@ -585,3 +585,8 @@ input[type="number"] {
|
|||||||
.form-small #currency {
|
.form-small #currency {
|
||||||
width: 10%;
|
width: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group-recurring {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
36
public/js/app.js
vendored
36
public/js/app.js
vendored
@ -186,6 +186,11 @@ $(document).ready(function () {
|
|||||||
disable_input.trigger('change');
|
disable_input.trigger('change');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (document.getElementById('recurring_frequency')) {
|
||||||
|
$(".input-group-recurring #recurring_frequency").select2();
|
||||||
|
$('.input-group-recurring #recurring_frequency').trigger('change');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function confirmDelete(form_id, title, message, button_cancel, button_delete) {
|
function confirmDelete(form_id, title, message, button_cancel, button_delete) {
|
||||||
@ -249,3 +254,34 @@ $(document).on('click', '.popup', function(e) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('change', '.input-group-recurring #recurring_frequency', function (e) {
|
||||||
|
var value = $(this).val();
|
||||||
|
|
||||||
|
var recurring_frequency = $('#recurring_frequency').parent().parent();
|
||||||
|
var recurring_interval = $('#recurring_interval').parent();
|
||||||
|
var recurring_custom_frequency = $('#recurring_custom_frequency').parent();
|
||||||
|
var recurring_count = $('#recurring_count').parent();
|
||||||
|
|
||||||
|
if (value == 'custom') {
|
||||||
|
recurring_frequency.removeClass('col-md-12').removeClass('col-md-12').addClass('col-md-4');
|
||||||
|
|
||||||
|
recurring_interval.removeClass('hidden');
|
||||||
|
recurring_custom_frequency.removeClass('hidden');
|
||||||
|
recurring_count.removeClass('hidden');
|
||||||
|
|
||||||
|
$("#recurring_custom_frequency").select2();
|
||||||
|
} else if (value == 'no' || value == '') {
|
||||||
|
recurring_frequency.removeClass('col-md-10').removeClass('col-md-4').addClass('col-md-12');
|
||||||
|
|
||||||
|
recurring_interval.addClass('hidden');
|
||||||
|
recurring_custom_frequency.addClass('hidden');
|
||||||
|
recurring_count.addClass('hidden');
|
||||||
|
} else {
|
||||||
|
recurring_frequency.removeClass('col-md-12').removeClass('col-md-4').addClass('col-md-10');
|
||||||
|
|
||||||
|
recurring_interval.addClass('hidden');
|
||||||
|
recurring_custom_frequency.addClass('hidden');
|
||||||
|
recurring_count.removeClass('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
19
resources/lang/en-GB/recurring.php
Normal file
19
resources/lang/en-GB/recurring.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'recurring' => 'Recurring',
|
||||||
|
'every' => 'Every',
|
||||||
|
'period' => 'Period',
|
||||||
|
'times' => 'Times',
|
||||||
|
'daily' => 'Daily',
|
||||||
|
'weekly' => 'Weekly',
|
||||||
|
'monthly' => 'Monthly',
|
||||||
|
'yearly' => 'Yearly',
|
||||||
|
'custom' => 'Custom',
|
||||||
|
'days' => 'Day(s)',
|
||||||
|
'weeks' => 'Week(s)',
|
||||||
|
'months' => 'Month(s)',
|
||||||
|
'years' => 'Year(s)',
|
||||||
|
|
||||||
|
];
|
@ -61,7 +61,7 @@
|
|||||||
<input class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
<input class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, setting('general.default_tax'), ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control select2', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, setting('general.default_tax'), ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control tax-select2', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right" style="vertical-align: middle;">
|
<td class="text-right" style="vertical-align: middle;">
|
||||||
<span id="item-total-{{ $item_row }}">0</span>
|
<span id="item-total-{{ $item_row }}">0</span>
|
||||||
@ -98,6 +98,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
||||||
|
|
||||||
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
||||||
{!! Form::label('category_id', trans_choice('general.categories', 1), ['class' => 'control-label']) !!}
|
{!! Form::label('category_id', trans_choice('general.categories', 1), ['class' => 'control-label']) !!}
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -110,7 +112,7 @@
|
|||||||
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
{{ Form::recurring('create') }}
|
||||||
|
|
||||||
{{ Form::fileGroup('attachment', trans('general.attachment'),[]) }}
|
{{ Form::fileGroup('attachment', trans('general.attachment'),[]) }}
|
||||||
</div>
|
</div>
|
||||||
@ -193,7 +195,7 @@
|
|||||||
autoclose: true
|
autoclose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".select2").select2({
|
$(".tax-select2").select2({
|
||||||
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
<input class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
<input class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, null, ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control select2', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, null, ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control tax-select2', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right" style="vertical-align: middle;">
|
<td class="text-right" style="vertical-align: middle;">
|
||||||
<span id="item-total-{{ $item_row }}">0</span>
|
<span id="item-total-{{ $item_row }}">0</span>
|
||||||
@ -114,9 +114,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
||||||
|
|
||||||
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
{{ Form::recurring('edit', $bill) }}
|
||||||
|
|
||||||
{{ Form::fileGroup('attachment', trans('general.attachment'),[]) }}
|
{{ Form::fileGroup('attachment', trans('general.attachment'),[]) }}
|
||||||
</div>
|
</div>
|
||||||
@ -201,7 +203,7 @@
|
|||||||
autoclose: true
|
autoclose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".select2").select2({
|
$(".tax-select2").select2({
|
||||||
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,6 +24,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
{!! Form::label('vendor_id', trans_choice('general.vendors', 1), ['class' => 'control-label']) !!}
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-addon"><i class="fa fa-user"></i></div>
|
||||||
|
{!! Form::select('vendor_id', $vendors, null, array_merge(['id' => 'vendor_id', 'class' => 'form-control', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.vendors', 1)])])) !!}
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" onclick="createVendor();" class="btn btn-default btn-icon"><i class="fa fa-plus"></i></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{ Form::textareaGroup('description', trans('general.description')) }}
|
{{ Form::textareaGroup('description', trans('general.description')) }}
|
||||||
|
|
||||||
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
||||||
@ -38,16 +49,7 @@
|
|||||||
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-6">
|
{{ Form::recurring('create') }}
|
||||||
{!! Form::label('vendor_id', trans_choice('general.vendors', 1), ['class' => 'control-label']) !!}
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon"><i class="fa fa-user"></i></div>
|
|
||||||
{!! Form::select('vendor_id', $vendors, null, array_merge(['id' => 'vendor_id', 'class' => 'form-control', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.vendors', 1)])])) !!}
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button type="button" onclick="createVendor();" class="btn btn-default btn-icon"><i class="fa fa-plus"></i></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods, setting('general.default_payment_method')) }}
|
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods, setting('general.default_payment_method')) }}
|
||||||
|
|
||||||
|
@ -29,11 +29,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ Form::selectGroup('vendor_id', trans_choice('general.vendors', 1), 'user', $vendors, null, []) }}
|
||||||
|
|
||||||
{{ Form::textareaGroup('description', trans('general.description')) }}
|
{{ Form::textareaGroup('description', trans('general.description')) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('vendor_id', trans_choice('general.vendors', 1), 'user', $vendors, null, []) }}
|
{{ Form::recurring('edit', $payment) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods) }}
|
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods) }}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<input class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
<input class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, setting('general.default_tax'), ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control select2', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, setting('general.default_tax'), ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control tax-select2', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right" style="vertical-align: middle;">
|
<td class="text-right" style="vertical-align: middle;">
|
||||||
<span id="item-total-{{ $item_row }}">0</span>
|
<span id="item-total-{{ $item_row }}">0</span>
|
||||||
@ -98,6 +98,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
||||||
|
|
||||||
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
||||||
{!! Form::label('category_id', trans_choice('general.categories', 1), ['class' => 'control-label']) !!}
|
{!! Form::label('category_id', trans_choice('general.categories', 1), ['class' => 'control-label']) !!}
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -110,7 +112,7 @@
|
|||||||
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
{{ Form::recurring('create') }}
|
||||||
|
|
||||||
{{ Form::fileGroup('attachment', trans('general.attachment')) }}
|
{{ Form::fileGroup('attachment', trans('general.attachment')) }}
|
||||||
</div>
|
</div>
|
||||||
@ -194,7 +196,7 @@
|
|||||||
autoclose: true
|
autoclose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".select2").select2({
|
$(".tax-select2").select2({
|
||||||
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<input value="{{ $item->price }}" class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
<input value="{{ $item->price }}" class="form-control text-right" required="required" name="item[{{ $item_row }}][price]" type="number" id="item-price-{{ $item_row }}">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, $item->tax_id, ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control select2', 'placeholder' => trans('general.form.enter', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
{!! Form::select('item[' . $item_row . '][tax_id]', $taxes, $item->tax_id, ['id'=> 'item-tax-'. $item_row, 'class' => 'form-control tax-select2', 'placeholder' => trans('general.form.enter', ['field' => trans_choice('general.taxes', 1)])]) !!}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right" style="vertical-align: middle;">
|
<td class="text-right" style="vertical-align: middle;">
|
||||||
<span id="item-total-{{ $item_row }}">@money($item->total, $invoice->currency_code, true)</span>
|
<span id="item-total-{{ $item_row }}">@money($item->total, $invoice->currency_code, true)</span>
|
||||||
@ -113,9 +113,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
||||||
|
|
||||||
{{ Form::textareaGroup('notes', trans_choice('general.notes', 2)) }}
|
{{ Form::recurring('edit', $invoice) }}
|
||||||
|
|
||||||
{{ Form::fileGroup('attachment', trans('general.attachment')) }}
|
{{ Form::fileGroup('attachment', trans('general.attachment')) }}
|
||||||
</div>
|
</div>
|
||||||
@ -200,7 +202,7 @@
|
|||||||
autoclose: true
|
autoclose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".select2").select2({
|
$(".tax-select2").select2({
|
||||||
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
placeholder: "{{ trans('general.form.select.field', ['field' => trans_choice('general.taxes', 1)]) }}"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,6 +24,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
{!! Form::label('customer_id', trans_choice('general.customers', 1), ['class' => 'control-label']) !!}
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-addon"><i class="fa fa-user"></i></div>
|
||||||
|
{!! Form::select('customer_id', $customers, null, array_merge(['class' => 'form-control', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.customers', 1)])])) !!}
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" onclick="createCustomer();" class="btn btn-default btn-icon"><i class="fa fa-plus"></i></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{ Form::textareaGroup('description', trans('general.description')) }}
|
{{ Form::textareaGroup('description', trans('general.description')) }}
|
||||||
|
|
||||||
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
<div class="form-group col-md-6 required {{ $errors->has('category_id') ? 'has-error' : ''}}">
|
||||||
@ -38,16 +49,7 @@
|
|||||||
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
{!! $errors->first('category_id', '<p class="help-block">:message</p>') !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-6">
|
{{ Form::recurring('create') }}
|
||||||
{!! Form::label('customer_id', trans_choice('general.customers', 1), ['class' => 'control-label']) !!}
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon"><i class="fa fa-user"></i></div>
|
|
||||||
{!! Form::select('customer_id', $customers, null, array_merge(['class' => 'form-control', 'placeholder' => trans('general.form.select.field', ['field' => trans_choice('general.customers', 1)])])) !!}
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button type="button" onclick="createCustomer();" class="btn btn-default btn-icon"><i class="fa fa-plus"></i></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods, setting('general.default_payment_method')) }}
|
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods, setting('general.default_payment_method')) }}
|
||||||
|
|
||||||
|
@ -29,11 +29,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ Form::selectGroup('customer_id', trans_choice('general.customers', 1), 'user', $customers, null, []) }}
|
||||||
|
|
||||||
{{ Form::textareaGroup('description', trans('general.description')) }}
|
{{ Form::textareaGroup('description', trans('general.description')) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
{{ Form::selectGroup('category_id', trans_choice('general.categories', 1), 'folder-open-o', $categories) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('customer_id', trans_choice('general.customers', 1), 'user', $customers, null, []) }}
|
{{ Form::recurring('edit', $revenue) }}
|
||||||
|
|
||||||
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods) }}
|
{{ Form::selectGroup('payment_method', trans_choice('general.payment_methods', 1), 'credit-card', $payment_methods) }}
|
||||||
|
|
||||||
|
43
resources/views/partials/form/recurring.blade.php
Normal file
43
resources/views/partials/form/recurring.blade.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
@php
|
||||||
|
if (($page == 'create') || !$model->recurring()->count()) {
|
||||||
|
$frequency = 'no';
|
||||||
|
$interval = 1;
|
||||||
|
$custom_frequency = 'monthly';
|
||||||
|
$count = 0;
|
||||||
|
} else {
|
||||||
|
$r = $model->recurring;
|
||||||
|
$frequency = ($r->interval != 1) ? 'custom' : $r->frequency;
|
||||||
|
$interval = $r->interval;
|
||||||
|
$custom_frequency = $r->frequency;
|
||||||
|
$count = $r->count;
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="col-md-6 input-group-recurring">
|
||||||
|
<div class="form-group col-md-12 {{ $errors->has('recurring_frequency') ? 'has-error' : ''}}">
|
||||||
|
{!! Form::label('recurring_frequency', trans('recurring.recurring'), ['class' => 'control-label']) !!}
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-addon"><i class="fa fa-refresh"></i></div>
|
||||||
|
{!! Form::select('recurring_frequency', $recurring_frequencies, $frequency, ['class' => 'form-control']) !!}
|
||||||
|
</div>
|
||||||
|
{!! $errors->first('recurring_frequency', '<p class="help-block">:message</p>') !!}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-2 hidden {{ $errors->has('recurring_interval') ? 'has-error' : '' }}">
|
||||||
|
{!! Form::label('recurring_interval', trans('recurring.every'), ['class' => 'control-label']) !!}
|
||||||
|
{!! Form::number('recurring_interval', $interval, ['class' => 'form-control']) !!}
|
||||||
|
{!! $errors->first('recurring_interval', '<p class="help-block">:message</p>') !!}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-4 hidden {{ $errors->has('recurring_custom_frequency') ? 'has-error' : ''}}">
|
||||||
|
{!! Form::label('recurring_custom_frequency', trans('recurring.period'), ['class' => 'control-label']) !!}
|
||||||
|
{!! Form::select('recurring_custom_frequency', $recurring_custom_frequencies, $custom_frequency, ['class' => 'form-control']) !!}
|
||||||
|
{!! $errors->first('recurring_custom_frequency', '<p class="help-block">:message</p>') !!}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-2 hidden {{ $errors->has('recurring_count') ? 'has-error' : '' }}">
|
||||||
|
{!! Form::label('recurring_count', trans('recurring.times'), ['class' => 'control-label']) !!}
|
||||||
|
{!! Form::number('recurring_count', $count, ['class' => 'form-control']) !!}
|
||||||
|
{!! $errors->first('recurring_count', '<p class="help-block">:message</p>') !!}
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user