commit
37ea9dcae2
192
app/Console/Commands/RecurringCheck.php
Normal file
192
app/Console/Commands/RecurringCheck.php
Normal file
@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company\Company;
|
||||
use App\Models\Expense\BillHistory;
|
||||
use App\Models\Income\InvoiceHistory;
|
||||
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()
|
||||
{
|
||||
$this->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();
|
||||
|
||||
foreach ($company->recurring as $recur) {
|
||||
if (!$current = $recur->current()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$current_date = Date::parse($current->format('Y-m-d'));
|
||||
|
||||
// Check if should recur today
|
||||
if ($this->today->ne($current_date)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$model = $recur->recurable;
|
||||
|
||||
if (!$model) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($recur->recurable_type) {
|
||||
case 'App\Models\Expense\Bill':
|
||||
$this->recurBill($company, $model);
|
||||
break;
|
||||
case 'App\Models\Income\Invoice':
|
||||
$this->recurInvoice($company, $model);
|
||||
break;
|
||||
case 'App\Models\Expense\Payment':
|
||||
case 'App\Models\Income\Revenue':
|
||||
$model->cloneable_relations = [];
|
||||
|
||||
// Create new record
|
||||
$clone = $model->duplicate();
|
||||
|
||||
$clone->parent_id = $model->id;
|
||||
$clone->paid_at = $this->today->format('Y-m-d');
|
||||
$clone->save();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unset company_id
|
||||
session()->forget('company_id');
|
||||
}
|
||||
|
||||
protected function recurInvoice($company, $model)
|
||||
{
|
||||
$model->cloneable_relations = ['items', 'totals'];
|
||||
|
||||
// Create new record
|
||||
$clone = $model->duplicate();
|
||||
|
||||
// Set original invoice id
|
||||
$clone->parent_id = $model->id;
|
||||
|
||||
// Days between invoiced and due date
|
||||
$diff_days = Date::parse($clone->due_at)->diffInDays(Date::parse($clone->invoiced_at));
|
||||
|
||||
// Update dates
|
||||
$clone->invoiced_at = $this->today->format('Y-m-d');
|
||||
$clone->due_at = $this->today->addDays($diff_days)->format('Y-m-d');
|
||||
$clone->save();
|
||||
|
||||
// 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)
|
||||
{
|
||||
$model->cloneable_relations = ['items', 'totals'];
|
||||
|
||||
// Create new record
|
||||
$clone = $model->duplicate();
|
||||
|
||||
// Set original bill id
|
||||
$clone->parent_id = $model->id;
|
||||
|
||||
// Days between invoiced and due date
|
||||
$diff_days = Date::parse($clone->due_at)->diffInDays(Date::parse($clone->invoiced_at));
|
||||
|
||||
// Update dates
|
||||
$clone->billed_at = $this->today->format('Y-m-d');
|
||||
$clone->due_at = $this->today->addDays($diff_days)->format('Y-m-d');
|
||||
$clone->save();
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ class Kernel extends ConsoleKernel
|
||||
Commands\Install::class,
|
||||
Commands\InvoiceReminder::class,
|
||||
Commands\ModuleInstall::class,
|
||||
Commands\RecurringCheck::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -35,6 +36,7 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->command('reminder:invoice')->dailyAt(setting('general.schedule_time', '09:00'));
|
||||
$schedule->command('reminder:bill')->dailyAt(setting('general.schedule_time', '09:00'));
|
||||
$schedule->command('recurring:check')->dailyAt(setting('general.schedule_time', '09:00'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,6 +67,7 @@ class Bills extends ApiController
|
||||
|
||||
$item_id = $item['item_id'];
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
|
||||
// Increase stock (item bought)
|
||||
@ -143,6 +144,7 @@ class Bills extends ApiController
|
||||
|
||||
$item_id = $item['item_id'];
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
} elseif (!empty($item['sku'])) {
|
||||
$item_sku = $item['sku'];
|
||||
|
@ -36,11 +36,18 @@ class Invoices extends ApiController
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param $id
|
||||
* @return \Dingo\Api\Http\Response
|
||||
*/
|
||||
public function show(Invoice $invoice)
|
||||
public function show($id)
|
||||
{
|
||||
// Check if we're querying by id or number
|
||||
if (is_numeric($id)) {
|
||||
$invoice = Invoice::find($id);
|
||||
} else {
|
||||
$invoice = Invoice::where('invoice_number', $id)->first();
|
||||
}
|
||||
|
||||
return $this->response->item($invoice, new Transformer());
|
||||
}
|
||||
|
||||
@ -76,6 +83,7 @@ class Invoices extends ApiController
|
||||
|
||||
$item_id = $item['item_id'];
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
|
||||
// Decrease stock (item sold)
|
||||
@ -194,6 +202,7 @@ class Invoices extends ApiController
|
||||
|
||||
$item_id = $item['item_id'];
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
} elseif (!empty($item['sku'])) {
|
||||
$item_sku = $item['sku'];
|
||||
|
@ -22,6 +22,16 @@ class Accounts extends Controller
|
||||
return view('banking.accounts.index', compact('accounts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('banking/accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -84,19 +94,34 @@ class Accounts extends Controller
|
||||
*/
|
||||
public function update(Account $account, Request $request)
|
||||
{
|
||||
$account->update($request->all());
|
||||
|
||||
// Set default account
|
||||
if ($request['default_account']) {
|
||||
setting()->set('general.default_account', $account->id);
|
||||
setting()->save();
|
||||
// Check if we can disable it
|
||||
if (!$request['enabled']) {
|
||||
if ($account->id == setting('general.default_account')) {
|
||||
$relationships[] = strtolower(trans_choice('general.companies', 1));
|
||||
}
|
||||
}
|
||||
|
||||
$message = trans('messages.success.updated', ['type' => trans_choice('general.accounts', 1)]);
|
||||
if (empty($relationships)) {
|
||||
$account->update($request->all());
|
||||
|
||||
flash($message)->success();
|
||||
// Set default account
|
||||
if ($request['default_account']) {
|
||||
setting()->set('general.default_account', $account->id);
|
||||
setting()->save();
|
||||
}
|
||||
|
||||
return redirect('banking/accounts');
|
||||
$message = trans('messages.success.updated', ['type' => trans_choice('general.accounts', 1)]);
|
||||
|
||||
flash($message)->success();
|
||||
|
||||
return redirect('banking/accounts');
|
||||
} else {
|
||||
$message = trans('messages.warning.disabled', ['name' => $account->name, 'text' => implode(', ', $relationships)]);
|
||||
|
||||
flash($message)->warning();
|
||||
|
||||
return redirect('banking/accounts/' . $account->id . '/edit');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,6 +140,10 @@ class Accounts extends Controller
|
||||
'revenues' => 'revenues',
|
||||
]);
|
||||
|
||||
if ($account->id == setting('general.default_account')) {
|
||||
$relationships[] = strtolower(trans_choice('general.companies', 1));
|
||||
}
|
||||
|
||||
if (empty($relationships)) {
|
||||
$account->delete();
|
||||
|
||||
|
@ -72,6 +72,16 @@ class Transfers extends Controller
|
||||
return view('banking.transfers.index', compact('transfers', 'items', 'accounts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('banking/transfers');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
|
@ -28,6 +28,17 @@ class Companies extends Controller
|
||||
|
||||
return view('companies.companies.index', compact('companies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('companies/companies');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
|
@ -243,52 +243,63 @@ class Dashboard extends Controller
|
||||
|
||||
private function calculateAmounts()
|
||||
{
|
||||
$incomes_amount = $expenses_amount = 0;
|
||||
$incomes_amount = $open_invoice = $overdue_invoice = 0;
|
||||
$expenses_amount = $open_bill = $overdue_bill = 0;
|
||||
|
||||
// Invoices
|
||||
$invoices = Invoice::with('payments')->accrued()->get();
|
||||
list($invoice_paid_amount, $open_invoice, $overdue_invoice) = $this->calculateTotals($invoices, 'invoice');
|
||||
|
||||
$incomes_amount += $invoice_paid_amount;
|
||||
|
||||
// Add to Incomes By Category
|
||||
$this->addToIncomeDonut('#00c0ef', $invoice_paid_amount, trans_choice('general.invoices', 2));
|
||||
|
||||
// Bills
|
||||
$bills = Bill::with('payments')->accrued()->get();
|
||||
list($bill_paid_amount, $open_bill, $overdue_bill) = $this->calculateTotals($bills, 'bill');
|
||||
|
||||
$expenses_amount += $bill_paid_amount;
|
||||
|
||||
// Add to Expenses By Category
|
||||
$this->addToExpenseDonut('#dd4b39', $bill_paid_amount, trans_choice('general.bills', 2));
|
||||
|
||||
// Revenues & Payments
|
||||
$categories = Category::orWhere('type', 'income')->orWhere('type', 'expense')->enabled()->get();
|
||||
// Get categories
|
||||
$categories = Category::with(['bills', 'invoices', 'payments', 'revenues'])->orWhere('type', 'income')->orWhere('type', 'expense')->enabled()->get();
|
||||
|
||||
foreach ($categories as $category) {
|
||||
switch ($category->type) {
|
||||
case 'income':
|
||||
$amount = 0;
|
||||
|
||||
// Revenues
|
||||
foreach ($category->revenues as $revenue) {
|
||||
$amount += $revenue->getConvertedAmount();
|
||||
}
|
||||
|
||||
$incomes_amount += $amount;
|
||||
|
||||
// Invoices
|
||||
$invoices = $category->invoices()->accrued()->get();
|
||||
foreach ($invoices as $invoice) {
|
||||
list($paid, $open, $overdue) = $this->calculateInvoiceBillTotals($invoice, 'invoice');
|
||||
|
||||
$incomes_amount += $paid;
|
||||
$open_invoice += $open;
|
||||
$overdue_invoice += $overdue;
|
||||
|
||||
$amount += $paid;
|
||||
}
|
||||
|
||||
$this->addToIncomeDonut($category->color, $amount, $category->name);
|
||||
|
||||
$incomes_amount += $amount;
|
||||
break;
|
||||
case 'expense':
|
||||
$amount = 0;
|
||||
|
||||
// Payments
|
||||
foreach ($category->payments as $payment) {
|
||||
$amount += $payment->getConvertedAmount();
|
||||
}
|
||||
|
||||
$expenses_amount += $amount;
|
||||
|
||||
// Bills
|
||||
$bills = $category->bills()->accrued()->get();
|
||||
foreach ($bills as $bill) {
|
||||
list($paid, $open, $overdue) = $this->calculateInvoiceBillTotals($bill, 'bill');
|
||||
|
||||
$expenses_amount += $paid;
|
||||
$open_bill += $open;
|
||||
$overdue_bill += $overdue;
|
||||
|
||||
$amount += $paid;
|
||||
}
|
||||
|
||||
$this->addToExpenseDonut($category->color, $amount, $category->name);
|
||||
|
||||
$expenses_amount += $amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -359,6 +370,10 @@ class Dashboard extends Controller
|
||||
$i = Date::parse($item->paid_at)->quarter;
|
||||
}
|
||||
|
||||
if (!isset($totals[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$totals[$i] += $item->getConvertedAmount();
|
||||
}
|
||||
}
|
||||
@ -378,22 +393,19 @@ class Dashboard extends Controller
|
||||
return $profit;
|
||||
}
|
||||
|
||||
private function calculateTotals($items, $type)
|
||||
private function calculateInvoiceBillTotals($item, $type)
|
||||
{
|
||||
$paid = $open = $overdue = 0;
|
||||
|
||||
$today = $this->today->toDateString();
|
||||
|
||||
foreach ($items as $item) {
|
||||
$paid += $item->getConvertedAmount();
|
||||
$paid += $item->getConvertedAmount();
|
||||
|
||||
$code_field = $type . '_status_code';
|
||||
|
||||
if ($item->$code_field == 'paid') {
|
||||
continue;
|
||||
}
|
||||
$code_field = $type . '_status_code';
|
||||
|
||||
if ($item->$code_field != 'paid') {
|
||||
$payments = 0;
|
||||
|
||||
if ($item->$code_field == 'partial') {
|
||||
foreach ($item->payments as $payment) {
|
||||
$payments += $payment->getConvertedAmount();
|
||||
|
@ -100,9 +100,11 @@ class Bills extends Controller
|
||||
|
||||
$items = Item::enabled()->pluck('name', 'id');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
return view('expenses.bills.create', compact('vendors', 'currencies', 'items', 'taxes'));
|
||||
$categories = Category::enabled()->type('expense')->pluck('name', 'id');
|
||||
|
||||
return view('expenses.bills.create', compact('vendors', 'currencies', 'items', 'taxes', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,6 +148,8 @@ class Bills extends Controller
|
||||
|
||||
$tax_total = 0;
|
||||
$sub_total = 0;
|
||||
$discount_total = 0;
|
||||
$discount = $request['discount'];
|
||||
|
||||
$bill_item = [];
|
||||
$bill_item['company_id'] = $request['company_id'];
|
||||
@ -159,6 +163,7 @@ class Bills extends Controller
|
||||
if (!empty($item['item_id'])) {
|
||||
$item_object = Item::find($item['item_id']);
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
|
||||
// Increase stock (item bought)
|
||||
@ -173,17 +178,22 @@ class Bills extends Controller
|
||||
|
||||
$tax_id = $item['tax_id'];
|
||||
|
||||
$tax = (($item['price'] * $item['quantity']) / 100) * $tax_object->rate;
|
||||
$tax = (((double) $item['price'] * (double) $item['quantity']) / 100) * $tax_object->rate;
|
||||
|
||||
// Apply discount to tax
|
||||
if ($discount) {
|
||||
$tax = $tax - ($tax * ($discount / 100));
|
||||
}
|
||||
}
|
||||
|
||||
$bill_item['item_id'] = $item['item_id'];
|
||||
$bill_item['name'] = str_limit($item['name'], 180, '');
|
||||
$bill_item['sku'] = $item_sku;
|
||||
$bill_item['quantity'] = $item['quantity'];
|
||||
$bill_item['price'] = $item['price'];
|
||||
$bill_item['quantity'] = (double) $item['quantity'];
|
||||
$bill_item['price'] = (double) $item['price'];
|
||||
$bill_item['tax'] = $tax;
|
||||
$bill_item['tax_id'] = $tax_id;
|
||||
$bill_item['total'] = $item['price'] * $item['quantity'];
|
||||
$bill_item['total'] = (double) $item['price'] * (double) $item['quantity'];
|
||||
|
||||
BillItem::create($bill_item);
|
||||
|
||||
@ -207,12 +217,21 @@ class Bills extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$request['amount'] += $sub_total + $tax_total;
|
||||
$s_total = $sub_total;
|
||||
|
||||
// Apply discount to total
|
||||
if ($discount) {
|
||||
$s_discount = $s_total * ($discount / 100);
|
||||
$discount_total += $s_discount;
|
||||
$s_total = $s_total - $s_discount;
|
||||
}
|
||||
|
||||
$request['amount'] = $s_total + $tax_total;
|
||||
|
||||
$bill->update($request->input());
|
||||
|
||||
// Add bill totals
|
||||
$this->addTotals($bill, $request, $taxes, $sub_total, $tax_total);
|
||||
$this->addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
||||
|
||||
// Add bill history
|
||||
BillHistory::create([
|
||||
@ -223,6 +242,9 @@ class Bills extends Controller
|
||||
'description' => trans('messages.success.added', ['type' => $bill->bill_number]),
|
||||
]);
|
||||
|
||||
// Recurring
|
||||
$bill->createRecurring();
|
||||
|
||||
// Fire the event to make it extendible
|
||||
event(new BillCreated($bill));
|
||||
|
||||
@ -300,9 +322,11 @@ class Bills extends Controller
|
||||
|
||||
$items = Item::enabled()->pluck('name', 'id');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
return view('expenses.bills.edit', compact('bill', 'vendors', 'currencies', 'items', 'taxes'));
|
||||
$categories = Category::enabled()->type('expense')->pluck('name', 'id');
|
||||
|
||||
return view('expenses.bills.edit', compact('bill', 'vendors', 'currencies', 'items', 'taxes', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,6 +357,8 @@ class Bills extends Controller
|
||||
$taxes = [];
|
||||
$tax_total = 0;
|
||||
$sub_total = 0;
|
||||
$discount_total = 0;
|
||||
$discount = $request['discount'];
|
||||
|
||||
$bill_item = [];
|
||||
$bill_item['company_id'] = $request['company_id'];
|
||||
@ -348,6 +374,7 @@ class Bills extends Controller
|
||||
if (!empty($item['item_id'])) {
|
||||
$item_object = Item::find($item['item_id']);
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
}
|
||||
|
||||
@ -358,17 +385,22 @@ class Bills extends Controller
|
||||
|
||||
$tax_id = $item['tax_id'];
|
||||
|
||||
$tax = (($item['price'] * $item['quantity']) / 100) * $tax_object->rate;
|
||||
$tax = (((double) $item['price'] * (double) $item['quantity']) / 100) * $tax_object->rate;
|
||||
|
||||
// Apply discount to tax
|
||||
if ($discount) {
|
||||
$tax = $tax - ($tax * ($discount / 100));
|
||||
}
|
||||
}
|
||||
|
||||
$bill_item['item_id'] = $item['item_id'];
|
||||
$bill_item['name'] = str_limit($item['name'], 180, '');
|
||||
$bill_item['sku'] = $item_sku;
|
||||
$bill_item['quantity'] = $item['quantity'];
|
||||
$bill_item['price'] = $item['price'];
|
||||
$bill_item['quantity'] = (double) $item['quantity'];
|
||||
$bill_item['price'] = (double) $item['price'];
|
||||
$bill_item['tax'] = $tax;
|
||||
$bill_item['tax_id'] = $tax_id;
|
||||
$bill_item['total'] = $item['price'] * $item['quantity'];
|
||||
$bill_item['total'] = (double) $item['price'] * (double) $item['quantity'];
|
||||
|
||||
if (isset($tax_object)) {
|
||||
if (array_key_exists($tax_object->id, $taxes)) {
|
||||
@ -388,7 +420,16 @@ class Bills extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$request['amount'] = $sub_total + $tax_total;
|
||||
$s_total = $sub_total;
|
||||
|
||||
// Apply discount to total
|
||||
if ($discount) {
|
||||
$s_discount = $s_total * ($discount / 100);
|
||||
$discount_total += $s_discount;
|
||||
$s_total = $s_total - $s_discount;
|
||||
}
|
||||
|
||||
$request['amount'] = $s_total + $tax_total;
|
||||
|
||||
$bill->update($request->input());
|
||||
|
||||
@ -403,7 +444,11 @@ class Bills extends Controller
|
||||
BillTotal::where('bill_id', $bill->id)->delete();
|
||||
|
||||
// Add bill totals
|
||||
$this->addTotals($bill, $request, $taxes, $sub_total, $tax_total);
|
||||
$bill->totals()->delete();
|
||||
$this->addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
||||
|
||||
// Recurring
|
||||
$bill->updateRecurring();
|
||||
|
||||
// Fire the event to make it extendible
|
||||
event(new BillUpdated($bill));
|
||||
@ -424,6 +469,7 @@ class Bills extends Controller
|
||||
*/
|
||||
public function destroy(Bill $bill)
|
||||
{
|
||||
$bill->recurring()->delete();
|
||||
$bill->delete();
|
||||
|
||||
/*
|
||||
@ -472,9 +518,7 @@ class Bills extends Controller
|
||||
{
|
||||
$bill = $this->prepareBill($bill);
|
||||
|
||||
$logo = $this->getLogo($bill);
|
||||
|
||||
return view($bill->template_path, compact('bill', 'logo'));
|
||||
return view($bill->template_path, compact('bill'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -488,9 +532,7 @@ class Bills extends Controller
|
||||
{
|
||||
$bill = $this->prepareBill($bill);
|
||||
|
||||
$logo = $this->getLogo($bill);
|
||||
|
||||
$html = view($bill->template_path, compact('bill', 'logo'))->render();
|
||||
$html = view($bill->template_path, compact('bill'))->render();
|
||||
|
||||
$pdf = \App::make('dompdf.wrapper');
|
||||
$pdf->loadHTML($html);
|
||||
@ -536,7 +578,7 @@ class Bills extends Controller
|
||||
}
|
||||
|
||||
if ($amount > $total_amount) {
|
||||
$message = trans('messages.error.payment_add');
|
||||
$message = trans('messages.error.over_payment');
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
@ -569,7 +611,7 @@ class Bills extends Controller
|
||||
|
||||
BillHistory::create($request->input());
|
||||
|
||||
$message = trans('messages.success.added', ['type' => trans_choice('general.revenues', 1)]);
|
||||
$message = trans('messages.success.added', ['type' => trans_choice('general.payments', 1)]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
@ -589,25 +631,23 @@ class Bills extends Controller
|
||||
{
|
||||
$bill = Bill::find($payment->bill_id);
|
||||
|
||||
$status = 'received';
|
||||
|
||||
if ($bill->payments()->count() > 1) {
|
||||
$bill->bill_status_code = 'partial';
|
||||
} else {
|
||||
$bill->bill_status_code = 'received';
|
||||
}
|
||||
|
||||
$bill->bill_status_code = $status;
|
||||
|
||||
$bill->save();
|
||||
|
||||
$desc_amount = money((float) $payment->amount, (string) $payment->currency_code, true)->format();
|
||||
|
||||
$description = $desc_amount . ' ' . trans_choice('general.payments', 1);
|
||||
|
||||
// Add invoice history
|
||||
// Add bill history
|
||||
BillHistory::create([
|
||||
'company_id' => $bill->company_id,
|
||||
'invoice_id' => $bill->id,
|
||||
'status_code' => $status,
|
||||
'bill_id' => $bill->id,
|
||||
'status_code' => $bill->bill_status_code,
|
||||
'notify' => 0,
|
||||
'description' => trans('messages.success.deleted', ['type' => $description]),
|
||||
]);
|
||||
@ -640,11 +680,11 @@ class Bills extends Controller
|
||||
return $bill;
|
||||
}
|
||||
|
||||
protected function addTotals($bill, $request, $taxes, $sub_total, $tax_total)
|
||||
protected function addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total)
|
||||
{
|
||||
$sort_order = 1;
|
||||
|
||||
// Added bill total sub total
|
||||
// Added bill sub total
|
||||
BillTotal::create([
|
||||
'company_id' => $request['company_id'],
|
||||
'bill_id' => $bill->id,
|
||||
@ -656,7 +696,24 @@ class Bills extends Controller
|
||||
|
||||
$sort_order++;
|
||||
|
||||
// Added bill total taxes
|
||||
// Added bill discount
|
||||
if ($discount_total) {
|
||||
BillTotal::create([
|
||||
'company_id' => $request['company_id'],
|
||||
'bill_id' => $bill->id,
|
||||
'code' => 'discount',
|
||||
'name' => 'bills.discount',
|
||||
'amount' => $discount_total,
|
||||
'sort_order' => $sort_order,
|
||||
]);
|
||||
|
||||
// This is for total
|
||||
$sub_total = $sub_total - $discount_total;
|
||||
}
|
||||
|
||||
$sort_order++;
|
||||
|
||||
// Added bill taxes
|
||||
if ($taxes) {
|
||||
foreach ($taxes as $tax) {
|
||||
BillTotal::create([
|
||||
@ -672,7 +729,7 @@ class Bills extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Added bill total total
|
||||
// Added bill total
|
||||
BillTotal::create([
|
||||
'company_id' => $request['company_id'],
|
||||
'bill_id' => $bill->id,
|
||||
@ -682,39 +739,4 @@ class Bills extends Controller
|
||||
'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;
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,16 @@ class Payments extends Controller
|
||||
return view('expenses.payments.index', compact('payments', 'vendors', 'categories', 'accounts', 'transfer_cat_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('expenses/payments');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -86,6 +96,9 @@ class Payments extends Controller
|
||||
$payment->attachMedia($media, 'attachment');
|
||||
}
|
||||
|
||||
// Recurring
|
||||
$payment->createRecurring();
|
||||
|
||||
$message = trans('messages.success.added', ['type' => trans_choice('general.payments', 1)]);
|
||||
|
||||
flash($message)->success();
|
||||
@ -185,6 +198,9 @@ class Payments extends Controller
|
||||
$payment->attachMedia($media, 'attachment');
|
||||
}
|
||||
|
||||
// Recurring
|
||||
$payment->updateRecurring();
|
||||
|
||||
$message = trans('messages.success.updated', ['type' => trans_choice('general.payments', 1)]);
|
||||
|
||||
flash($message)->success();
|
||||
@ -206,6 +222,7 @@ class Payments extends Controller
|
||||
return redirect('expenses/payments');
|
||||
}
|
||||
|
||||
$payment->recurring()->delete();
|
||||
$payment->delete();
|
||||
|
||||
$message = trans('messages.success.deleted', ['type' => trans_choice('general.payments', 1)]);
|
||||
|
@ -4,10 +4,16 @@ namespace App\Http\Controllers\Expenses;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Expense\Vendor as Request;
|
||||
use App\Models\Expense\Bill;
|
||||
use App\Models\Expense\Payment;
|
||||
use App\Models\Expense\Vendor;
|
||||
use App\Models\Setting\Currency;
|
||||
use App\Traits\Uploads;
|
||||
use App\Utilities\ImportFile;
|
||||
use Date;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class Vendors extends Controller
|
||||
{
|
||||
@ -25,6 +31,78 @@ class Vendors extends Controller
|
||||
return view('expenses.vendors.index', compact('vendors'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @param Vendor $vendor
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show(Vendor $vendor)
|
||||
{
|
||||
$amounts = [
|
||||
'paid' => 0,
|
||||
'open' => 0,
|
||||
'overdue' => 0,
|
||||
];
|
||||
|
||||
$counts = [
|
||||
'bills' => 0,
|
||||
'payments' => 0,
|
||||
];
|
||||
|
||||
// Handle bills
|
||||
$bills = Bill::with(['status', 'payments'])->where('vendor_id', $vendor->id)->get();
|
||||
|
||||
$counts['bills'] = $bills->count();
|
||||
|
||||
$bill_payments = [];
|
||||
|
||||
$today = Date::today()->toDateString();
|
||||
|
||||
foreach ($bills as $item) {
|
||||
$payments = 0;
|
||||
|
||||
foreach ($item->payments as $payment) {
|
||||
$payment->category = $item->category;
|
||||
|
||||
$bill_payments[] = $payment;
|
||||
|
||||
$amount = $payment->getConvertedAmount();
|
||||
|
||||
$amounts['paid'] += $amount;
|
||||
|
||||
$payments += $amount;
|
||||
}
|
||||
|
||||
if ($item->bill_status_code == 'paid') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if it's open or overdue invoice
|
||||
if ($item->due_at > $today) {
|
||||
$amounts['open'] += $item->getConvertedAmount() - $payments;
|
||||
} else {
|
||||
$amounts['overdue'] += $item->getConvertedAmount() - $payments;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle payments
|
||||
$payments = Payment::with(['account', 'category'])->where('vendor_id', $vendor->id)->get();
|
||||
|
||||
$counts['payments'] = $payments->count();
|
||||
|
||||
// Prepare data
|
||||
$items = collect($payments)->each(function ($item) use (&$amounts) {
|
||||
$amounts['paid'] += $item->getConvertedAmount();
|
||||
});
|
||||
|
||||
$limit = request('limit', setting('general.list_limit', '25'));
|
||||
$transactions = $this->paginate($items->merge($bill_payments)->sortByDesc('paid_at'), $limit);
|
||||
|
||||
return view('expenses.vendors.show', compact('vendor', 'counts', 'amounts', 'transactions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -206,4 +284,23 @@ class Vendors extends Controller
|
||||
|
||||
return response()->json($vendor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a pagination collection.
|
||||
*
|
||||
* @param array|Collection $items
|
||||
* @param int $perPage
|
||||
* @param int $page
|
||||
* @param array $options
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function paginate($items, $perPage = 15, $page = null, $options = [])
|
||||
{
|
||||
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
|
||||
|
||||
$items = $items instanceof Collection ? $items : Collection::make($items);
|
||||
|
||||
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,17 @@ namespace App\Http\Controllers\Incomes;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Income\Customer as Request;
|
||||
use Illuminate\Http\Request as FRequest;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Income\Customer;
|
||||
use App\Models\Income\Invoice;
|
||||
use App\Models\Income\Revenue;
|
||||
use App\Models\Setting\Currency;
|
||||
use App\Utilities\ImportFile;
|
||||
use Date;
|
||||
use Illuminate\Http\Request as FRequest;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class Customers extends Controller
|
||||
{
|
||||
@ -25,6 +31,78 @@ class Customers extends Controller
|
||||
return view('incomes.customers.index', compact('customers', 'emails'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @param Customer $customer
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show(Customer $customer)
|
||||
{
|
||||
$amounts = [
|
||||
'paid' => 0,
|
||||
'open' => 0,
|
||||
'overdue' => 0,
|
||||
];
|
||||
|
||||
$counts = [
|
||||
'invoices' => 0,
|
||||
'revenues' => 0,
|
||||
];
|
||||
|
||||
// Handle invoices
|
||||
$invoices = Invoice::with(['status', 'payments'])->where('customer_id', $customer->id)->get();
|
||||
|
||||
$counts['invoices'] = $invoices->count();
|
||||
|
||||
$invoice_payments = [];
|
||||
|
||||
$today = Date::today()->toDateString();
|
||||
|
||||
foreach ($invoices as $item) {
|
||||
$payments = 0;
|
||||
|
||||
foreach ($item->payments as $payment) {
|
||||
$payment->category = $item->category;
|
||||
|
||||
$invoice_payments[] = $payment;
|
||||
|
||||
$amount = $payment->getConvertedAmount();
|
||||
|
||||
$amounts['paid'] += $amount;
|
||||
|
||||
$payments += $amount;
|
||||
}
|
||||
|
||||
if ($item->invoice_status_code == 'paid') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if it's open or overdue invoice
|
||||
if ($item->due_at > $today) {
|
||||
$amounts['open'] += $item->getConvertedAmount() - $payments;
|
||||
} else {
|
||||
$amounts['overdue'] += $item->getConvertedAmount() - $payments;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle revenues
|
||||
$revenues = Revenue::with(['account', 'category'])->where('customer_id', $customer->id)->get();
|
||||
|
||||
$counts['revenues'] = $revenues->count();
|
||||
|
||||
// Prepare data
|
||||
$items = collect($revenues)->each(function ($item) use (&$amounts) {
|
||||
$amounts['paid'] += $item->getConvertedAmount();
|
||||
});
|
||||
|
||||
$limit = request('limit', setting('general.list_limit', '25'));
|
||||
$transactions = $this->paginate($items->merge($invoice_payments)->sortByDesc('paid_at'), $limit);
|
||||
|
||||
return view('incomes.customers.show', compact('customer', 'counts', 'amounts', 'transactions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -266,4 +344,23 @@ class Customers extends Controller
|
||||
|
||||
return response()->json($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a pagination collection.
|
||||
*
|
||||
* @param array|Collection $items
|
||||
* @param int $perPage
|
||||
* @param int $page
|
||||
* @param array $options
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function paginate($items, $perPage = 15, $page = null, $options = [])
|
||||
{
|
||||
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
|
||||
|
||||
$items = $items instanceof Collection ? $items : Collection::make($items);
|
||||
|
||||
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
|
||||
}
|
||||
}
|
||||
|
@ -103,11 +103,13 @@ class Invoices extends Controller
|
||||
|
||||
$items = Item::enabled()->pluck('name', 'id');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
$categories = Category::enabled()->type('income')->pluck('name', 'id');
|
||||
|
||||
$number = $this->getNextInvoiceNumber();
|
||||
|
||||
return view('incomes.invoices.create', compact('customers', 'currencies', 'items', 'taxes', 'number'));
|
||||
return view('incomes.invoices.create', compact('customers', 'currencies', 'items', 'taxes', 'categories', 'number'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,6 +153,8 @@ class Invoices extends Controller
|
||||
|
||||
$tax_total = 0;
|
||||
$sub_total = 0;
|
||||
$discount_total = 0;
|
||||
$discount = $request['discount'];
|
||||
|
||||
$invoice_item = [];
|
||||
$invoice_item['company_id'] = $request['company_id'];
|
||||
@ -163,6 +167,7 @@ class Invoices extends Controller
|
||||
if (!empty($item['item_id'])) {
|
||||
$item_object = Item::find($item['item_id']);
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
|
||||
// Decrease stock (item sold)
|
||||
@ -188,17 +193,22 @@ class Invoices extends Controller
|
||||
|
||||
$tax_id = $item['tax_id'];
|
||||
|
||||
$tax = (($item['price'] * $item['quantity']) / 100) * $tax_object->rate;
|
||||
$tax = (((double) $item['price'] * (double) $item['quantity']) / 100) * $tax_object->rate;
|
||||
|
||||
// Apply discount to tax
|
||||
if ($discount) {
|
||||
$tax = $tax - ($tax * ($discount / 100));
|
||||
}
|
||||
}
|
||||
|
||||
$invoice_item['item_id'] = $item['item_id'];
|
||||
$invoice_item['name'] = str_limit($item['name'], 180, '');
|
||||
$invoice_item['sku'] = $item_sku;
|
||||
$invoice_item['quantity'] = $item['quantity'];
|
||||
$invoice_item['price'] = $item['price'];
|
||||
$invoice_item['quantity'] = (double) $item['quantity'];
|
||||
$invoice_item['price'] = (double) $item['price'];
|
||||
$invoice_item['tax'] = $tax;
|
||||
$invoice_item['tax_id'] = $tax_id;
|
||||
$invoice_item['total'] = $item['price'] * $item['quantity'];
|
||||
$invoice_item['total'] = (double) $item['price'] * (double) $item['quantity'];
|
||||
|
||||
InvoiceItem::create($invoice_item);
|
||||
|
||||
@ -222,12 +232,21 @@ class Invoices extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$request['amount'] = $sub_total + $tax_total;
|
||||
$s_total = $sub_total;
|
||||
|
||||
// Apply discount to total
|
||||
if ($discount) {
|
||||
$s_discount = $s_total * ($discount / 100);
|
||||
$discount_total += $s_discount;
|
||||
$s_total = $s_total - $s_discount;
|
||||
}
|
||||
|
||||
$request['amount'] = $s_total + $tax_total;
|
||||
|
||||
$invoice->update($request->input());
|
||||
|
||||
// Add invoice totals
|
||||
$this->addTotals($invoice, $request, $taxes, $sub_total, $tax_total);
|
||||
$this->addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
||||
|
||||
// Add invoice history
|
||||
InvoiceHistory::create([
|
||||
@ -241,6 +260,9 @@ class Invoices extends Controller
|
||||
// Update next invoice number
|
||||
$this->increaseNextInvoiceNumber();
|
||||
|
||||
// Recurring
|
||||
$invoice->createRecurring();
|
||||
|
||||
// Fire the event to make it extendible
|
||||
event(new InvoiceCreated($invoice));
|
||||
|
||||
@ -321,9 +343,11 @@ class Invoices extends Controller
|
||||
|
||||
$items = Item::enabled()->pluck('name', 'id');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
return view('incomes.invoices.edit', compact('invoice', 'customers', 'currencies', 'items', 'taxes'));
|
||||
$categories = Category::enabled()->type('income')->pluck('name', 'id');
|
||||
|
||||
return view('incomes.invoices.edit', compact('invoice', 'customers', 'currencies', 'items', 'taxes', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,6 +378,8 @@ class Invoices extends Controller
|
||||
$taxes = [];
|
||||
$tax_total = 0;
|
||||
$sub_total = 0;
|
||||
$discount_total = 0;
|
||||
$discount = $request['discount'];
|
||||
|
||||
$invoice_item = [];
|
||||
$invoice_item['company_id'] = $request['company_id'];
|
||||
@ -369,6 +395,7 @@ class Invoices extends Controller
|
||||
if (!empty($item['item_id'])) {
|
||||
$item_object = Item::find($item['item_id']);
|
||||
|
||||
$item['name'] = $item_object->name;
|
||||
$item_sku = $item_object->sku;
|
||||
}
|
||||
|
||||
@ -379,17 +406,22 @@ class Invoices extends Controller
|
||||
|
||||
$tax_id = $item['tax_id'];
|
||||
|
||||
$tax = (($item['price'] * $item['quantity']) / 100) * $tax_object->rate;
|
||||
$tax = (((double) $item['price'] * (double) $item['quantity']) / 100) * $tax_object->rate;
|
||||
|
||||
// Apply discount to tax
|
||||
if ($discount) {
|
||||
$tax = $tax - ($tax * ($discount / 100));
|
||||
}
|
||||
}
|
||||
|
||||
$invoice_item['item_id'] = $item['item_id'];
|
||||
$invoice_item['name'] = str_limit($item['name'], 180, '');
|
||||
$invoice_item['sku'] = $item_sku;
|
||||
$invoice_item['quantity'] = $item['quantity'];
|
||||
$invoice_item['price'] = $item['price'];
|
||||
$invoice_item['quantity'] = (double) $item['quantity'];
|
||||
$invoice_item['price'] = (double) $item['price'];
|
||||
$invoice_item['tax'] = $tax;
|
||||
$invoice_item['tax_id'] = $tax_id;
|
||||
$invoice_item['total'] = $item['price'] * $item['quantity'];
|
||||
$invoice_item['total'] = (double) $item['price'] * (double) $item['quantity'];
|
||||
|
||||
if (isset($tax_object)) {
|
||||
if (array_key_exists($tax_object->id, $taxes)) {
|
||||
@ -409,7 +441,16 @@ class Invoices extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$request['amount'] = $sub_total + $tax_total;
|
||||
$s_total = $sub_total;
|
||||
|
||||
// Apply discount to total
|
||||
if ($discount) {
|
||||
$s_discount = $s_total * ($discount / 100);
|
||||
$discount_total += $s_discount;
|
||||
$s_total = $s_total - $s_discount;
|
||||
}
|
||||
|
||||
$request['amount'] = $s_total + $tax_total;
|
||||
|
||||
$invoice->update($request->input());
|
||||
|
||||
@ -424,7 +465,11 @@ class Invoices extends Controller
|
||||
InvoiceTotal::where('invoice_id', $invoice->id)->delete();
|
||||
|
||||
// Add invoice totals
|
||||
$this->addTotals($invoice, $request, $taxes, $sub_total, $tax_total);
|
||||
$invoice->totals()->delete();
|
||||
$this->addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total);
|
||||
|
||||
// Recurring
|
||||
$invoice->updateRecurring();
|
||||
|
||||
// Fire the event to make it extendible
|
||||
event(new InvoiceUpdated($invoice));
|
||||
@ -445,6 +490,7 @@ class Invoices extends Controller
|
||||
*/
|
||||
public function destroy(Invoice $invoice)
|
||||
{
|
||||
$invoice->recurring()->delete();
|
||||
$invoice->delete();
|
||||
|
||||
/*
|
||||
@ -507,9 +553,7 @@ class Invoices extends Controller
|
||||
|
||||
$invoice = $this->prepareInvoice($invoice);
|
||||
|
||||
$logo = $this->getLogo();
|
||||
|
||||
$html = view($invoice->template_path, compact('invoice', 'logo'))->render();
|
||||
$html = view($invoice->template_path, compact('invoice'))->render();
|
||||
|
||||
$pdf = \App::make('dompdf.wrapper');
|
||||
$pdf->loadHTML($html);
|
||||
@ -563,9 +607,7 @@ class Invoices extends Controller
|
||||
{
|
||||
$invoice = $this->prepareInvoice($invoice);
|
||||
|
||||
$logo = $this->getLogo();
|
||||
|
||||
return view($invoice->template_path, compact('invoice', 'logo'));
|
||||
return view($invoice->template_path, compact('invoice'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -579,9 +621,7 @@ class Invoices extends Controller
|
||||
{
|
||||
$invoice = $this->prepareInvoice($invoice);
|
||||
|
||||
$logo = $this->getLogo();
|
||||
|
||||
$html = view($invoice->template_path, compact('invoice', 'logo'))->render();
|
||||
$html = view($invoice->template_path, compact('invoice'))->render();
|
||||
|
||||
$pdf = \App::make('dompdf.wrapper');
|
||||
$pdf->loadHTML($html);
|
||||
@ -669,7 +709,7 @@ class Invoices extends Controller
|
||||
}
|
||||
|
||||
if ($amount > $total_amount) {
|
||||
$message = trans('messages.error.payment_add');
|
||||
$message = trans('messages.error.over_payment');
|
||||
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
@ -702,7 +742,7 @@ class Invoices extends Controller
|
||||
|
||||
InvoiceHistory::create($request->input());
|
||||
|
||||
$message = trans('messages.success.added', ['type' => trans_choice('general.revenues', 1)]);
|
||||
$message = trans('messages.success.added', ['type' => trans_choice('general.payments', 1)]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
@ -722,14 +762,12 @@ class Invoices extends Controller
|
||||
{
|
||||
$invoice = Invoice::find($payment->invoice_id);
|
||||
|
||||
$status = 'sent';
|
||||
|
||||
if ($invoice->payments()->count() > 1) {
|
||||
$status = 'partial';
|
||||
$invoice->invoice_status_code = 'partial';
|
||||
} else {
|
||||
$invoice->invoice_status_code = 'sent';
|
||||
}
|
||||
|
||||
$invoice->invoice_status_code = $status;
|
||||
|
||||
$invoice->save();
|
||||
|
||||
$desc_amount = money((float) $payment->amount, (string) $payment->currency_code, true)->format();
|
||||
@ -740,7 +778,7 @@ class Invoices extends Controller
|
||||
InvoiceHistory::create([
|
||||
'company_id' => $invoice->company_id,
|
||||
'invoice_id' => $invoice->id,
|
||||
'status_code' => $status,
|
||||
'status_code' => $invoice->invoice_status_code,
|
||||
'notify' => 0,
|
||||
'description' => trans('messages.success.deleted', ['type' => $description]),
|
||||
]);
|
||||
@ -773,11 +811,11 @@ class Invoices extends Controller
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
protected function addTotals($invoice, $request, $taxes, $sub_total, $tax_total)
|
||||
protected function addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total)
|
||||
{
|
||||
$sort_order = 1;
|
||||
|
||||
// Added invoice total sub total
|
||||
// Added invoice sub total
|
||||
InvoiceTotal::create([
|
||||
'company_id' => $request['company_id'],
|
||||
'invoice_id' => $invoice->id,
|
||||
@ -789,7 +827,24 @@ class Invoices extends Controller
|
||||
|
||||
$sort_order++;
|
||||
|
||||
// Added invoice total taxes
|
||||
// Added invoice discount
|
||||
if ($discount_total) {
|
||||
InvoiceTotal::create([
|
||||
'company_id' => $request['company_id'],
|
||||
'invoice_id' => $invoice->id,
|
||||
'code' => 'discount',
|
||||
'name' => 'invoices.discount',
|
||||
'amount' => $discount_total,
|
||||
'sort_order' => $sort_order,
|
||||
]);
|
||||
|
||||
// This is for total
|
||||
$sub_total = $sub_total - $discount_total;
|
||||
}
|
||||
|
||||
$sort_order++;
|
||||
|
||||
// Added invoice taxes
|
||||
if ($taxes) {
|
||||
foreach ($taxes as $tax) {
|
||||
InvoiceTotal::create([
|
||||
@ -805,7 +860,7 @@ class Invoices extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Added invoice total total
|
||||
// Added invoice total
|
||||
InvoiceTotal::create([
|
||||
'company_id' => $request['company_id'],
|
||||
'invoice_id' => $invoice->id,
|
||||
@ -815,39 +870,4 @@ class Invoices extends Controller
|
||||
'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;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,16 @@ class Revenues extends Controller
|
||||
return view('incomes.revenues.index', compact('revenues', 'customers', 'categories', 'accounts', 'transfer_cat_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('incomes/revenues');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -88,6 +98,9 @@ class Revenues extends Controller
|
||||
$revenue->attachMedia($media, 'attachment');
|
||||
}
|
||||
|
||||
// Recurring
|
||||
$revenue->createRecurring();
|
||||
|
||||
$message = trans('messages.success.added', ['type' => trans_choice('general.revenues', 1)]);
|
||||
|
||||
flash($message)->success();
|
||||
@ -187,6 +200,9 @@ class Revenues extends Controller
|
||||
$revenue->attachMedia($media, 'attachment');
|
||||
}
|
||||
|
||||
// Recurring
|
||||
$revenue->updateRecurring();
|
||||
|
||||
$message = trans('messages.success.updated', ['type' => trans_choice('general.revenues', 1)]);
|
||||
|
||||
flash($message)->success();
|
||||
@ -208,6 +224,7 @@ class Revenues extends Controller
|
||||
return redirect('incomes/revenues');
|
||||
}
|
||||
|
||||
$revenue->recurring()->delete();
|
||||
$revenue->delete();
|
||||
|
||||
$message = trans('messages.success.deleted', ['type' => trans_choice('general.revenues', 1)]);
|
||||
|
@ -30,6 +30,16 @@ class Items extends Controller
|
||||
return view('items.items.index', compact('items', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('items/items');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -39,7 +49,7 @@ class Items extends Controller
|
||||
{
|
||||
$categories = Category::enabled()->type('item')->pluck('name', 'id');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
return view('items.items.create', compact('categories', 'taxes'));
|
||||
}
|
||||
@ -123,7 +133,7 @@ class Items extends Controller
|
||||
{
|
||||
$categories = Category::enabled()->type('item')->pluck('name', 'id');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
return view('items.items.edit', compact('item', 'categories', 'taxes'));
|
||||
}
|
||||
@ -195,9 +205,10 @@ class Items extends Controller
|
||||
|
||||
$currency = Currency::where('code', $currency_code)->first();
|
||||
|
||||
$filter_data = array(
|
||||
'name' => $query
|
||||
);
|
||||
$filter_data = [
|
||||
'name' => $query,
|
||||
'sku' => $query,
|
||||
];
|
||||
|
||||
$items = Item::getItems($filter_data);
|
||||
|
||||
@ -235,6 +246,7 @@ class Items extends Controller
|
||||
{
|
||||
$input_items = request('item');
|
||||
$currency_code = request('currency_code');
|
||||
$discount = request('discount');
|
||||
|
||||
if (empty($currency_code)) {
|
||||
$currency_code = setting('general.default_currency');
|
||||
@ -250,7 +262,7 @@ class Items extends Controller
|
||||
if ($input_items) {
|
||||
foreach ($input_items as $key => $item) {
|
||||
$price = (double) $item['price'];
|
||||
$quantity = (int) $item['quantity'];
|
||||
$quantity = (double) $item['quantity'];
|
||||
|
||||
$item_tax_total= 0;
|
||||
$item_sub_total = ($price * $quantity);
|
||||
@ -262,11 +274,15 @@ class Items extends Controller
|
||||
}
|
||||
|
||||
$sub_total += $item_sub_total;
|
||||
|
||||
// Apply discount to tax
|
||||
if ($discount) {
|
||||
$item_tax_total = $item_tax_total - ($item_tax_total * ($discount / 100));
|
||||
}
|
||||
|
||||
$tax_total += $item_tax_total;
|
||||
|
||||
$total = $item_sub_total + $item_tax_total;
|
||||
|
||||
$items[$key] = money($total, $currency_code, true)->format();
|
||||
$items[$key] = money($item_sub_total, $currency_code, true)->format();
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,8 +290,19 @@ class Items extends Controller
|
||||
|
||||
$json->sub_total = money($sub_total, $currency_code, true)->format();
|
||||
|
||||
$json->discount_text= trans('invoices.add_discount');
|
||||
$json->discount_total = '';
|
||||
|
||||
$json->tax_total = money($tax_total, $currency_code, true)->format();
|
||||
|
||||
// Apply discount to total
|
||||
if ($discount) {
|
||||
$json->discount_text= trans('invoices.show_discount', ['discount' => $discount]);
|
||||
$json->discount_total = money($sub_total * ($discount / 100), $currency_code, true)->format();
|
||||
|
||||
$sub_total = $sub_total - ($sub_total * ($discount / 100));
|
||||
}
|
||||
|
||||
$grand_total = $sub_total + $tax_total;
|
||||
|
||||
$json->grand_total = money($grand_total, $currency_code, true)->format();
|
||||
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers\Modules;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Modules;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Tiles extends Controller
|
||||
{
|
||||
@ -73,4 +74,27 @@ class Tiles extends Controller
|
||||
|
||||
return view('modules.tiles.index', compact('title', 'modules'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function searchModules(Request $request)
|
||||
{
|
||||
$this->checkApiToken();
|
||||
|
||||
$keyword = $request['keyword'];
|
||||
|
||||
$data = [
|
||||
'query' => [
|
||||
'keyword' => $keyword,
|
||||
]
|
||||
];
|
||||
|
||||
$title = trans('modules.search');
|
||||
$modules = $this->getSearchModules($data);
|
||||
|
||||
return view('modules.tiles.index', compact('title', 'modules', 'keyword'));
|
||||
}
|
||||
}
|
||||
|
@ -23,12 +23,7 @@ class ExpenseSummary extends Controller
|
||||
|
||||
$status = request('status');
|
||||
|
||||
//if ($filter != 'upcoming') {
|
||||
$categories = Category::enabled()->type('expense')->pluck('name', 'id')->toArray();
|
||||
//}
|
||||
|
||||
// Add Bill in Categories
|
||||
$categories[0] = trans_choice('general.bills', 2);
|
||||
$categories = Category::enabled()->type('expense')->pluck('name', 'id')->toArray();
|
||||
|
||||
// Get year
|
||||
$year = request('year');
|
||||
@ -49,15 +44,6 @@ class ExpenseSummary extends Controller
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
// Bill
|
||||
$expenses[0][$dates[$j]] = array(
|
||||
'category_id' => 0,
|
||||
'name' => trans_choice('general.bills', 1),
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
foreach ($categories as $category_id => $category_name) {
|
||||
$expenses[$category_id][$dates[$j]] = array(
|
||||
'category_id' => $category_id,
|
||||
@ -87,10 +73,19 @@ class ExpenseSummary extends Controller
|
||||
|
||||
// Payments
|
||||
if ($status != 'upcoming') {
|
||||
$payments = Payment::monthsOfYear('paid_at')->get();
|
||||
$payments = Payment::monthsOfYear('paid_at')->isNotTransfer()->get();
|
||||
$this->setAmount($expenses_graph, $totals, $expenses, $payments, 'payment', 'paid_at');
|
||||
}
|
||||
|
||||
// Check if it's a print or normal request
|
||||
if (request('print')) {
|
||||
$chart_template = 'vendor.consoletvs.charts.chartjs.multi.line_print';
|
||||
$view_template = 'reports.expense_summary.print';
|
||||
} else {
|
||||
$chart_template = 'vendor.consoletvs.charts.chartjs.multi.line';
|
||||
$view_template = 'reports.expense_summary.index';
|
||||
}
|
||||
|
||||
// Expenses chart
|
||||
$chart = Charts::multi('line', 'chartjs')
|
||||
->dimensions(0, 300)
|
||||
@ -98,12 +93,9 @@ class ExpenseSummary extends Controller
|
||||
->dataset(trans_choice('general.expenses', 1), $expenses_graph)
|
||||
->labels($dates)
|
||||
->credits(false)
|
||||
->view('vendor.consoletvs.charts.chartjs.multi.line');
|
||||
->view($chart_template);
|
||||
|
||||
// Expenses Graph
|
||||
$expenses_graph = json_encode($expenses_graph);
|
||||
|
||||
return view('reports.expense_summary.index', compact('chart', 'dates', 'categories', 'expenses', 'totals'));
|
||||
return view($view_template, compact('chart', 'dates', 'categories', 'expenses', 'totals'));
|
||||
}
|
||||
|
||||
private function setAmount(&$graph, &$totals, &$expenses, $items, $type, $date_field)
|
||||
@ -111,13 +103,7 @@ class ExpenseSummary extends Controller
|
||||
foreach ($items as $item) {
|
||||
$date = Date::parse($item->$date_field)->format('F');
|
||||
|
||||
if ($type == 'bill') {
|
||||
$category_id = 0;
|
||||
} else {
|
||||
$category_id = $item->category_id;
|
||||
}
|
||||
|
||||
if (!isset($expenses[$category_id])) {
|
||||
if (!isset($expenses[$item->category_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -130,9 +116,9 @@ class ExpenseSummary extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$expenses[$category_id][$date]['amount'] += $amount;
|
||||
$expenses[$category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$expenses[$category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
$expenses[$item->category_id][$date]['amount'] += $amount;
|
||||
$expenses[$item->category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$expenses[$item->category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
|
||||
$graph[Date::parse($item->$date_field)->format('F-Y')] += $amount;
|
||||
|
||||
|
@ -26,19 +26,9 @@ class IncomeExpenseSummary extends Controller
|
||||
|
||||
$status = request('status');
|
||||
|
||||
//if ($filter != 'upcoming') {
|
||||
$income_categories = Category::enabled()->type('income')->pluck('name', 'id')->toArray();
|
||||
//}
|
||||
$income_categories = Category::enabled()->type('income')->pluck('name', 'id')->toArray();
|
||||
|
||||
// Add Invoice in Categories
|
||||
$income_categories[0] = trans_choice('general.invoices', 2);
|
||||
|
||||
//if ($filter != 'upcoming') {
|
||||
$expense_categories = Category::enabled()->type('expense')->pluck('name', 'id')->toArray();
|
||||
//}
|
||||
|
||||
// Add Bill in Categories
|
||||
$expense_categories[0] = trans_choice('general.bills', 2);
|
||||
$expense_categories = Category::enabled()->type('expense')->pluck('name', 'id')->toArray();
|
||||
|
||||
// Get year
|
||||
$year = request('year');
|
||||
@ -59,15 +49,6 @@ class IncomeExpenseSummary extends Controller
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
// Compares
|
||||
$compares['income'][0][$dates[$j]] = array(
|
||||
'category_id' => 0,
|
||||
'name' => trans_choice('general.invoices', 1),
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
foreach ($income_categories as $category_id => $category_name) {
|
||||
$compares['income'][$category_id][$dates[$j]] = array(
|
||||
'category_id' => $category_id,
|
||||
@ -78,14 +59,6 @@ class IncomeExpenseSummary extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
$compares['expense'][0][$dates[$j]] = array(
|
||||
'category_id' => 0,
|
||||
'name' => trans_choice('general.invoices', 1),
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
foreach ($expense_categories as $category_id => $category_name) {
|
||||
$compares['expense'][$category_id][$dates[$j]] = array(
|
||||
'category_id' => $category_id,
|
||||
@ -115,7 +88,7 @@ class IncomeExpenseSummary extends Controller
|
||||
|
||||
// Revenues
|
||||
if ($status != 'upcoming') {
|
||||
$revenues = Revenue::monthsOfYear('paid_at')->get();
|
||||
$revenues = Revenue::monthsOfYear('paid_at')->isNotTransfer()->get();
|
||||
$this->setAmount($profit_graph, $totals, $compares, $revenues, 'revenue', 'paid_at');
|
||||
}
|
||||
|
||||
@ -137,10 +110,19 @@ class IncomeExpenseSummary extends Controller
|
||||
|
||||
// Payments
|
||||
if ($status != 'upcoming') {
|
||||
$payments = Payment::monthsOfYear('paid_at')->get();
|
||||
$payments = Payment::monthsOfYear('paid_at')->isNotTransfer()->get();
|
||||
$this->setAmount($profit_graph, $totals, $compares, $payments, 'payment', 'paid_at');
|
||||
}
|
||||
|
||||
// Check if it's a print or normal request
|
||||
if (request('print')) {
|
||||
$chart_template = 'vendor.consoletvs.charts.chartjs.multi.line_print';
|
||||
$view_template = 'reports.income_expense_summary.print';
|
||||
} else {
|
||||
$chart_template = 'vendor.consoletvs.charts.chartjs.multi.line';
|
||||
$view_template = 'reports.income_expense_summary.index';
|
||||
}
|
||||
|
||||
// Profit chart
|
||||
$chart = Charts::multi('line', 'chartjs')
|
||||
->dimensions(0, 300)
|
||||
@ -148,9 +130,9 @@ class IncomeExpenseSummary extends Controller
|
||||
->dataset(trans_choice('general.profits', 1), $profit_graph)
|
||||
->labels($dates)
|
||||
->credits(false)
|
||||
->view('vendor.consoletvs.charts.chartjs.multi.line');
|
||||
->view($chart_template);
|
||||
|
||||
return view('reports.income_expense_summary.index', compact('chart', 'dates', 'income_categories', 'expense_categories', 'compares', 'totals'));
|
||||
return view($view_template, compact('chart', 'dates', 'income_categories', 'expense_categories', 'compares', 'totals'));
|
||||
}
|
||||
|
||||
private function setAmount(&$graph, &$totals, &$compares, $items, $type, $date_field)
|
||||
@ -158,15 +140,9 @@ class IncomeExpenseSummary extends Controller
|
||||
foreach ($items as $item) {
|
||||
$date = Date::parse($item->$date_field)->format('F');
|
||||
|
||||
if (($type == 'invoice') || ($type == 'bill')) {
|
||||
$category_id = 0;
|
||||
} else {
|
||||
$category_id = $item->category_id;
|
||||
}
|
||||
$group = (($type == 'invoice') || ($type == 'revenue')) ? 'income' : 'expense';
|
||||
|
||||
$group = (($type == 'invoice') || ($type == 'revenue')) ? 'income' : 'expense';
|
||||
|
||||
if (!isset($compares[$group][$category_id])) {
|
||||
if (!isset($compares[$group][$item->category_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -179,9 +155,9 @@ class IncomeExpenseSummary extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$compares[$group][$category_id][$date]['amount'] += $amount;
|
||||
$compares[$group][$category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$compares[$group][$category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
$compares[$group][$item->category_id][$date]['amount'] += $amount;
|
||||
$compares[$group][$item->category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$compares[$group][$item->category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
|
||||
if ($group == 'income') {
|
||||
$graph[Date::parse($item->$date_field)->format('F-Y')] += $amount;
|
||||
|
@ -23,12 +23,7 @@ class IncomeSummary extends Controller
|
||||
|
||||
$status = request('status');
|
||||
|
||||
//if ($filter != 'upcoming') {
|
||||
$categories = Category::enabled()->type('income')->pluck('name', 'id')->toArray();
|
||||
//}
|
||||
|
||||
// Add Invoice in Categories
|
||||
$categories[0] = trans_choice('general.invoices', 2);
|
||||
$categories = Category::enabled()->type('income')->pluck('name', 'id')->toArray();
|
||||
|
||||
// Get year
|
||||
$year = request('year');
|
||||
@ -49,15 +44,6 @@ class IncomeSummary extends Controller
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
// Invoice
|
||||
$incomes[0][$dates[$j]] = array(
|
||||
'category_id' => 0,
|
||||
'name' => trans_choice('general.invoices', 1),
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
foreach ($categories as $category_id => $category_name) {
|
||||
$incomes[$category_id][$dates[$j]] = array(
|
||||
'category_id' => $category_id,
|
||||
@ -87,10 +73,19 @@ class IncomeSummary extends Controller
|
||||
|
||||
// Revenues
|
||||
if ($status != 'upcoming') {
|
||||
$revenues = Revenue::monthsOfYear('paid_at')->get();
|
||||
$revenues = Revenue::monthsOfYear('paid_at')->isNotTransfer()->get();
|
||||
$this->setAmount($incomes_graph, $totals, $incomes, $revenues, 'revenue', 'paid_at');
|
||||
}
|
||||
|
||||
// Check if it's a print or normal request
|
||||
if (request('print')) {
|
||||
$chart_template = 'vendor.consoletvs.charts.chartjs.multi.line_print';
|
||||
$view_template = 'reports.income_summary.print';
|
||||
} else {
|
||||
$chart_template = 'vendor.consoletvs.charts.chartjs.multi.line';
|
||||
$view_template = 'reports.income_summary.index';
|
||||
}
|
||||
|
||||
// Incomes chart
|
||||
$chart = Charts::multi('line', 'chartjs')
|
||||
->dimensions(0, 300)
|
||||
@ -98,9 +93,9 @@ class IncomeSummary extends Controller
|
||||
->dataset(trans_choice('general.incomes', 1), $incomes_graph)
|
||||
->labels($dates)
|
||||
->credits(false)
|
||||
->view('vendor.consoletvs.charts.chartjs.multi.line');
|
||||
->view($chart_template);
|
||||
|
||||
return view('reports.income_summary.index', compact('chart', 'dates', 'categories', 'incomes', 'totals'));
|
||||
return view($view_template, compact('chart', 'dates', 'categories', 'incomes', 'totals'));
|
||||
}
|
||||
|
||||
private function setAmount(&$graph, &$totals, &$incomes, $items, $type, $date_field)
|
||||
@ -108,13 +103,7 @@ class IncomeSummary extends Controller
|
||||
foreach ($items as $item) {
|
||||
$date = Date::parse($item->$date_field)->format('F');
|
||||
|
||||
if ($type == 'invoice') {
|
||||
$category_id = 0;
|
||||
} else {
|
||||
$category_id = $item->category_id;
|
||||
}
|
||||
|
||||
if (!isset($incomes[$category_id])) {
|
||||
if (!isset($incomes[$item->category_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -127,9 +116,9 @@ class IncomeSummary extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$incomes[$category_id][$date]['amount'] += $amount;
|
||||
$incomes[$category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$incomes[$category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
$incomes[$item->category_id][$date]['amount'] += $amount;
|
||||
$incomes[$item->category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$incomes[$item->category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
|
||||
$graph[Date::parse($item->$date_field)->format('F-Y')] += $amount;
|
||||
|
||||
|
189
app/Http/Controllers/Reports/ProfitLoss.php
Normal file
189
app/Http/Controllers/Reports/ProfitLoss.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Income\Invoice;
|
||||
use App\Models\Income\InvoicePayment;
|
||||
use App\Models\Income\Revenue;
|
||||
use App\Models\Expense\Bill;
|
||||
use App\Models\Expense\BillPayment;
|
||||
use App\Models\Expense\Payment;
|
||||
use App\Models\Setting\Category;
|
||||
use Charts;
|
||||
use Date;
|
||||
|
||||
class ProfitLoss extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$dates = $totals = $compares = $categories = [];
|
||||
|
||||
$status = request('status');
|
||||
|
||||
$income_categories = Category::enabled()->type('income')->pluck('name', 'id')->toArray();
|
||||
|
||||
$expense_categories = Category::enabled()->type('expense')->pluck('name', 'id')->toArray();
|
||||
|
||||
// Get year
|
||||
$year = request('year');
|
||||
if (empty($year)) {
|
||||
$year = Date::now()->year;
|
||||
}
|
||||
|
||||
// Dates
|
||||
for ($j = 1; $j <= 12; $j++) {
|
||||
$dates[$j] = Date::parse($year . '-' . $j)->quarter;
|
||||
|
||||
// Totals
|
||||
$totals[$dates[$j]] = array(
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
);
|
||||
|
||||
foreach ($income_categories as $category_id => $category_name) {
|
||||
$compares['income'][$category_id][$dates[$j]] = [
|
||||
'category_id' => $category_id,
|
||||
'name' => $category_name,
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($expense_categories as $category_id => $category_name) {
|
||||
$compares['expense'][$category_id][$dates[$j]] = [
|
||||
'category_id' => $category_id,
|
||||
'name' => $category_name,
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
];
|
||||
}
|
||||
|
||||
$j += 2;
|
||||
}
|
||||
|
||||
$totals['total'] = [
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
];
|
||||
|
||||
$gross['income'] = $gross['expense'] = [1 => 0, 2 => 0, 3 => 0, 4 => 0, 'total' => 0];
|
||||
|
||||
foreach ($income_categories as $category_id => $category_name) {
|
||||
$compares['income'][$category_id]['total'] = [
|
||||
'category_id' => $category_id,
|
||||
'name' => trans_choice('general.totals', 1),
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($expense_categories as $category_id => $category_name) {
|
||||
$compares['expense'][$category_id]['total'] = [
|
||||
'category_id' => $category_id,
|
||||
'name' => trans_choice('general.totals', 1),
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1
|
||||
];
|
||||
}
|
||||
|
||||
// Invoices
|
||||
switch ($status) {
|
||||
case 'paid':
|
||||
$invoices = InvoicePayment::monthsOfYear('paid_at')->get();
|
||||
$this->setAmount($totals, $compares, $invoices, 'invoice', 'paid_at');
|
||||
break;
|
||||
case 'upcoming':
|
||||
$invoices = Invoice::accrued()->monthsOfYear('due_at')->get();
|
||||
$this->setAmount($totals, $compares, $invoices, 'invoice', 'due_at');
|
||||
break;
|
||||
default:
|
||||
$invoices = Invoice::accrued()->monthsOfYear('invoiced_at')->get();
|
||||
$this->setAmount($totals, $compares, $invoices, 'invoice', 'invoiced_at');
|
||||
break;
|
||||
}
|
||||
|
||||
// Revenues
|
||||
if ($status != 'upcoming') {
|
||||
$revenues = Revenue::monthsOfYear('paid_at')->isNotTransfer()->get();
|
||||
$this->setAmount($totals, $compares, $revenues, 'revenue', 'paid_at');
|
||||
}
|
||||
|
||||
// Bills
|
||||
switch ($status) {
|
||||
case 'paid':
|
||||
$bills = BillPayment::monthsOfYear('paid_at')->get();
|
||||
$this->setAmount($totals, $compares, $bills, 'bill', 'paid_at');
|
||||
break;
|
||||
case 'upcoming':
|
||||
$bills = Bill::accrued()->monthsOfYear('due_at')->get();
|
||||
$this->setAmount($totals, $compares, $bills, 'bill', 'due_at');
|
||||
break;
|
||||
default:
|
||||
$bills = Bill::accrued()->monthsOfYear('billed_at')->get();
|
||||
$this->setAmount($totals, $compares, $bills, 'bill', 'billed_at');
|
||||
break;
|
||||
}
|
||||
|
||||
// Payments
|
||||
if ($status != 'upcoming') {
|
||||
$payments = Payment::monthsOfYear('paid_at')->isNotTransfer()->get();
|
||||
$this->setAmount($totals, $compares, $payments, 'payment', 'paid_at');
|
||||
}
|
||||
|
||||
// Check if it's a print or normal request
|
||||
if (request('print')) {
|
||||
$view_template = 'reports.profit_loss.print';
|
||||
} else {
|
||||
$view_template = 'reports.profit_loss.index';
|
||||
}
|
||||
|
||||
return view($view_template, compact('dates', 'income_categories', 'expense_categories', 'compares', 'totals', 'gross'));
|
||||
}
|
||||
|
||||
private function setAmount(&$totals, &$compares, $items, $type, $date_field)
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
$date = Date::parse($item->$date_field)->quarter;
|
||||
|
||||
$group = (($type == 'invoice') || ($type == 'revenue')) ? 'income' : 'expense';
|
||||
|
||||
if (!isset($compares[$group][$item->category_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$amount = $item->getConvertedAmount();
|
||||
|
||||
// Forecasting
|
||||
if ((($type == 'invoice') || ($type == 'bill')) && ($date_field == 'due_at')) {
|
||||
foreach ($item->payments as $payment) {
|
||||
$amount -= $payment->getConvertedAmount();
|
||||
}
|
||||
}
|
||||
|
||||
$compares[$group][$item->category_id][$date]['amount'] += $amount;
|
||||
$compares[$group][$item->category_id][$date]['currency_code'] = $item->currency_code;
|
||||
$compares[$group][$item->category_id][$date]['currency_rate'] = $item->currency_rate;
|
||||
$compares[$group][$item->category_id]['total']['amount'] += $amount;
|
||||
|
||||
if ($group == 'income') {
|
||||
$totals[$date]['amount'] += $amount;
|
||||
$totals['total']['amount'] += $amount;
|
||||
} else {
|
||||
$totals[$date]['amount'] -= $amount;
|
||||
$totals['total']['amount'] -= $amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
app/Http/Controllers/Reports/TaxSummary.php
Normal file
135
app/Http/Controllers/Reports/TaxSummary.php
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Reports;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Expense\Bill;
|
||||
use App\Models\Expense\BillPayment;
|
||||
use App\Models\Expense\BillTotal;
|
||||
use App\Models\Income\Invoice;
|
||||
use App\Models\Income\InvoicePayment;
|
||||
use App\Models\Income\InvoiceTotal;
|
||||
use App\Models\Setting\Tax;
|
||||
use App\Traits\Currencies;
|
||||
use Date;
|
||||
|
||||
class TaxSummary extends Controller
|
||||
{
|
||||
use Currencies;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$dates = $incomes = $expenses = $totals = [];
|
||||
|
||||
$status = request('status');
|
||||
|
||||
$t = Tax::enabled()->where('rate', '<>', '0')->pluck('name')->toArray();
|
||||
|
||||
$taxes = array_combine($t, $t);
|
||||
|
||||
// Get year
|
||||
$year = request('year');
|
||||
if (empty($year)) {
|
||||
$year = Date::now()->year;
|
||||
}
|
||||
|
||||
// Dates
|
||||
for ($j = 1; $j <= 12; $j++) {
|
||||
$dates[$j] = Date::parse($year . '-' . $j)->format('M');
|
||||
|
||||
foreach ($taxes as $tax_name) {
|
||||
$incomes[$tax_name][$dates[$j]] = [
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1,
|
||||
];
|
||||
|
||||
$expenses[$tax_name][$dates[$j]] = [
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1,
|
||||
];
|
||||
|
||||
$totals[$tax_name][$dates[$j]] = [
|
||||
'amount' => 0,
|
||||
'currency_code' => setting('general.default_currency'),
|
||||
'currency_rate' => 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
switch ($status) {
|
||||
case 'paid':
|
||||
// Invoices
|
||||
$invoices = InvoicePayment::with(['invoice', 'invoice.totals'])->monthsOfYear('paid_at')->get();
|
||||
$this->setAmount($incomes, $totals, $invoices, 'invoice', 'paid_at');
|
||||
// Bills
|
||||
$bills = BillPayment::with(['bill', 'bill.totals'])->monthsOfYear('paid_at')->get();
|
||||
$this->setAmount($expenses, $totals, $bills, 'bill', 'paid_at');
|
||||
break;
|
||||
case 'upcoming':
|
||||
// Invoices
|
||||
$invoices = Invoice::with(['totals'])->accrued()->monthsOfYear('due_at')->get();
|
||||
$this->setAmount($incomes, $totals, $invoices, 'invoice', 'due_at');
|
||||
// Bills
|
||||
$bills = Bill::with(['totals'])->accrued()->monthsOfYear('due_at')->get();
|
||||
$this->setAmount($expenses, $totals, $bills, 'bill', 'due_at');
|
||||
break;
|
||||
default:
|
||||
// Invoices
|
||||
$invoices = Invoice::with(['totals'])->accrued()->monthsOfYear('invoiced_at')->get();
|
||||
$this->setAmount($incomes, $totals, $invoices, 'invoice', 'invoiced_at');
|
||||
// Bills
|
||||
$bills = Bill::with(['totals'])->accrued()->monthsOfYear('billed_at')->get();
|
||||
$this->setAmount($expenses, $totals, $bills, 'bill', 'billed_at');
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if it's a print or normal request
|
||||
if (request('print')) {
|
||||
$view_template = 'reports.tax_summary.print';
|
||||
} else {
|
||||
$view_template = 'reports.tax_summary.index';
|
||||
}
|
||||
|
||||
return view($view_template, compact('dates', 'taxes', 'incomes', 'expenses', 'totals'));
|
||||
}
|
||||
|
||||
private function setAmount(&$items, &$totals, $rows, $type, $date_field)
|
||||
{
|
||||
foreach ($rows as $row) {
|
||||
$date = Date::parse($row->$date_field)->format('M');
|
||||
|
||||
if ($date_field == 'paid_at') {
|
||||
$row_totals = $row->$type->totals;
|
||||
} else {
|
||||
$row_totals = $row->totals;
|
||||
}
|
||||
|
||||
foreach ($row_totals as $row_total) {
|
||||
if ($row_total->code != 'tax') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($items[$row_total->name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$amount = $this->convert($row_total->amount, $row->currency_code, $row->currency_rate);
|
||||
|
||||
$items[$row_total->name][$date]['amount'] += $amount;
|
||||
|
||||
if ($type == 'invoice') {
|
||||
$totals[$row_total->name][$date]['amount'] += $amount;
|
||||
} else {
|
||||
$totals[$row_total->name][$date]['amount'] -= $amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,12 +20,26 @@ class Categories extends Controller
|
||||
|
||||
$transfer_id = Category::transfer();
|
||||
|
||||
$types = collect(['expense' => 'Expense', 'income' => 'Income', 'item' => 'Item', 'other' => 'Other'])
|
||||
->prepend(trans('general.all_type', ['type' => trans_choice('general.types', 2)]), '');
|
||||
$types = collect([
|
||||
'expense' => trans_choice('general.expenses', 1),
|
||||
'income' => trans_choice('general.incomes', 1),
|
||||
'item' => trans_choice('general.items', 1),
|
||||
'other' => trans_choice('general.others', 1),
|
||||
])->prepend(trans('general.all_type', ['type' => trans_choice('general.types', 2)]), '');
|
||||
|
||||
return view('settings.categories.index', compact('categories', 'types', 'transfer_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('settings/categories');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
@ -33,7 +47,14 @@ class Categories extends Controller
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('settings.categories.create');
|
||||
$types = [
|
||||
'expense' => trans_choice('general.expenses', 1),
|
||||
'income' => trans_choice('general.incomes', 1),
|
||||
'item' => trans_choice('general.items', 1),
|
||||
'other' => trans_choice('general.others', 1),
|
||||
];
|
||||
|
||||
return view('settings.categories.create', compact('types'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,7 +84,16 @@ class Categories extends Controller
|
||||
*/
|
||||
public function edit(Category $category)
|
||||
{
|
||||
return view('settings.categories.edit', compact('category'));
|
||||
$types = [
|
||||
'expense' => trans_choice('general.expenses', 1),
|
||||
'income' => trans_choice('general.incomes', 1),
|
||||
'item' => trans_choice('general.items', 1),
|
||||
'other' => trans_choice('general.others', 1),
|
||||
];
|
||||
|
||||
$type_disabled = (Category::where('type', $category->type)->count() == 1) ?: false;
|
||||
|
||||
return view('settings.categories.edit', compact('category', 'types', 'type_disabled'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,7 +108,9 @@ class Categories extends Controller
|
||||
{
|
||||
$relationships = $this->countRelationships($category, [
|
||||
'items' => 'items',
|
||||
'invoices' => 'invoices',
|
||||
'revenues' => 'revenues',
|
||||
'bills' => 'bills',
|
||||
'payments' => 'payments',
|
||||
]);
|
||||
|
||||
@ -108,17 +140,23 @@ class Categories extends Controller
|
||||
*/
|
||||
public function destroy(Category $category)
|
||||
{
|
||||
$relationships = $this->countRelationships($category, [
|
||||
'items' => 'items',
|
||||
'revenues' => 'revenues',
|
||||
'payments' => 'payments',
|
||||
]);
|
||||
// Can not delete the last category by type
|
||||
if (Category::where('type', $category->type)->count() == 1) {
|
||||
$message = trans('messages.error.last_category', ['type' => strtolower(trans_choice('general.' . $category->type . 's', 1))]);
|
||||
|
||||
flash($message)->warning();
|
||||
|
||||
// Can't delete transfer category
|
||||
if ($category->id == Category::transfer()) {
|
||||
return redirect('settings/categories');
|
||||
}
|
||||
|
||||
$relationships = $this->countRelationships($category, [
|
||||
'items' => 'items',
|
||||
'invoices' => 'invoices',
|
||||
'revenues' => 'revenues',
|
||||
'bills' => 'bills',
|
||||
'payments' => 'payments',
|
||||
]);
|
||||
|
||||
if (empty($relationships)) {
|
||||
$category->delete();
|
||||
|
||||
@ -133,4 +171,11 @@ class Categories extends Controller
|
||||
|
||||
return redirect('settings/categories');
|
||||
}
|
||||
|
||||
public function category(Request $request)
|
||||
{
|
||||
$category = Category::create($request->all());
|
||||
|
||||
return response()->json($category);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,16 @@ class Currencies extends Controller
|
||||
return view('settings.currencies.index', compact('currencies'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('settings/currencies');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
|
@ -47,7 +47,7 @@ class Settings extends Controller
|
||||
|
||||
$currencies = Currency::enabled()->pluck('name', 'code');
|
||||
|
||||
$taxes = Tax::enabled()->pluck('name', 'id');
|
||||
$taxes = Tax::enabled()->get()->pluck('title', 'id');
|
||||
|
||||
$payment_methods = Modules::getPaymentMethods();
|
||||
|
||||
@ -56,7 +56,7 @@ class Settings extends Controller
|
||||
'd F Y' => '31 December 2017',
|
||||
'd m Y' => '31 12 2017',
|
||||
'm d Y' => '12 31 2017',
|
||||
'Y m d' => '2017 12 31'
|
||||
'Y m d' => '2017 12 31',
|
||||
];
|
||||
|
||||
$date_separators = [
|
||||
@ -71,7 +71,12 @@ class Settings extends Controller
|
||||
'mail' => trans('settings.email.php'),
|
||||
'smtp' => trans('settings.email.smtp.name'),
|
||||
'sendmail' => trans('settings.email.sendmail'),
|
||||
'log' => trans('settings.email.log')
|
||||
'log' => trans('settings.email.log'),
|
||||
];
|
||||
|
||||
$percent_positions = [
|
||||
'before' => trans('settings.localisation.percent.before'),
|
||||
'after' => trans('settings.localisation.percent.after'),
|
||||
];
|
||||
|
||||
return view('settings.settings.edit', compact(
|
||||
@ -83,7 +88,8 @@ class Settings extends Controller
|
||||
'payment_methods',
|
||||
'date_formats',
|
||||
'date_separators',
|
||||
'email_protocols'
|
||||
'email_protocols',
|
||||
'percent_positions'
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,16 @@ class Taxes extends Controller
|
||||
return view('settings.taxes.index', compact('taxes', 'rates'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for viewing the specified resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
return redirect('settings/taxes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
|
@ -111,7 +111,13 @@ class AdminMenu
|
||||
}
|
||||
|
||||
// Reports
|
||||
if ($user->can(['read-reports-income-summary', 'read-reports-expense-summary', 'read-reports-income-expense-summary'])) {
|
||||
if ($user->can([
|
||||
'read-reports-income-summary',
|
||||
'read-reports-expense-summary',
|
||||
'read-reports-income-expense-summary',
|
||||
'read-reports-tax-summary',
|
||||
'read-reports-profit-loss',
|
||||
])) {
|
||||
$menu->dropdown(trans_choice('general.reports', 2), function ($sub) use($user, $attr) {
|
||||
if ($user->can('read-reports-income-summary')) {
|
||||
$sub->url('reports/income-summary', trans('reports.summary.income'), 1, $attr);
|
||||
@ -124,6 +130,14 @@ class AdminMenu
|
||||
if ($user->can('read-reports-income-expense-summary')) {
|
||||
$sub->url('reports/income-expense-summary', trans('reports.summary.income_expense'), 3, $attr);
|
||||
}
|
||||
|
||||
if ($user->can('read-reports-tax-summary')) {
|
||||
$sub->url('reports/tax-summary', trans('reports.summary.tax'), 4, $attr);
|
||||
}
|
||||
|
||||
if ($user->can('read-reports-profit-loss')) {
|
||||
$sub->url('reports/profit-loss', trans('reports.profit_loss'), 5, $attr);
|
||||
}
|
||||
}, 6, [
|
||||
'title' => trans_choice('general.reports', 2),
|
||||
'icon' => 'fa fa-bar-chart',
|
||||
|
@ -39,6 +39,7 @@ class Bill extends Request
|
||||
'billed_at' => 'required|date',
|
||||
'due_at' => 'required|date',
|
||||
'currency_code' => 'required|string',
|
||||
'category_id' => 'required|integer',
|
||||
'attachment' => 'mimes:' . setting('general.file_types') . '|between:0,' . setting('general.file_size') * 1024,
|
||||
];
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ class Invoice extends Request
|
||||
'invoiced_at' => 'required|date',
|
||||
'due_at' => 'required|date',
|
||||
'currency_code' => 'required|string',
|
||||
'category_id' => 'required|integer',
|
||||
'attachment' => 'mimes:' . setting('general.file_types') . '|between:0,' . setting('general.file_size') * 1024,
|
||||
];
|
||||
}
|
||||
|
@ -3,9 +3,26 @@
|
||||
namespace App\Http\Requests\Module;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Traits\Modules;
|
||||
use Illuminate\Validation\Factory as ValidationFactory;
|
||||
|
||||
class Module extends Request
|
||||
{
|
||||
use Modules;
|
||||
|
||||
public function __construct(ValidationFactory $validation)
|
||||
{
|
||||
|
||||
$validation->extend(
|
||||
'check',
|
||||
function ($attribute, $value, $parameters) {
|
||||
return $this->checkToken($value);
|
||||
},
|
||||
trans('messages.error.invalid_token')
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@ -24,7 +41,7 @@ class Module extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'api_token' => 'required|string',
|
||||
'api_token' => 'required|string|check',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class Tax extends Request
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string',
|
||||
'rate' => 'required',
|
||||
'rate' => 'required|min:0|max:100',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
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]);
|
||||
}
|
||||
}
|
137
app/Listeners/Updates/Version120.php
Normal file
137
app/Listeners/Updates/Version120.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners\Updates;
|
||||
|
||||
use App\Events\UpdateFinished;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Company\Company;
|
||||
use App\Models\Expense\Bill;
|
||||
use App\Models\Income\Invoice;
|
||||
use App\Models\Setting\Category;
|
||||
use DB;
|
||||
use Schema;
|
||||
use Artisan;
|
||||
|
||||
class Version120 extends Listener
|
||||
{
|
||||
const ALIAS = 'core';
|
||||
|
||||
const VERSION = '1.2.0';
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(UpdateFinished $event)
|
||||
{
|
||||
// Check if should listen
|
||||
if (!$this->check($event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->updatePermissions();
|
||||
|
||||
// Update database
|
||||
Artisan::call('migrate', ['--force' => true]);
|
||||
|
||||
$this->updateInvoicesAndBills();
|
||||
|
||||
$this->changeQuantityColumn();
|
||||
}
|
||||
|
||||
protected function updatePermissions()
|
||||
{
|
||||
$permissions = [];
|
||||
|
||||
// Create tax summary permission
|
||||
$permissions[] = Permission::firstOrCreate([
|
||||
'name' => 'read-reports-tax-summary',
|
||||
'display_name' => 'Read Reports Tax Summary',
|
||||
'description' => 'Read Reports Tax Summary',
|
||||
]);
|
||||
|
||||
// Create profit loss permission
|
||||
$permissions[] = Permission::firstOrCreate([
|
||||
'name' => 'read-reports-profit-loss',
|
||||
'display_name' => 'Read Reports Profit Loss',
|
||||
'description' => 'Read Reports Profit Loss',
|
||||
]);
|
||||
|
||||
// Attach permission to roles
|
||||
$roles = Role::all();
|
||||
|
||||
foreach ($roles as $role) {
|
||||
$allowed = ['admin', 'manager'];
|
||||
|
||||
if (!in_array($role->name, $allowed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
$role->attachPermission($permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateInvoicesAndBills()
|
||||
{
|
||||
$companies = Company::all();
|
||||
|
||||
foreach ($companies as $company) {
|
||||
// Invoices
|
||||
$invoice_category = Category::create([
|
||||
'company_id' => $company->id,
|
||||
'name' => trans_choice('general.invoices', 2),
|
||||
'type' => 'income',
|
||||
'color' => '#00c0ef',
|
||||
'enabled' => '1'
|
||||
]);
|
||||
|
||||
foreach ($company->invoices as $invoice) {
|
||||
$invoice->category_id = $invoice_category->id;
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
// Bills
|
||||
$bill_category = Category::create([
|
||||
'company_id' => $company->id,
|
||||
'name' => trans_choice('general.bills', 2),
|
||||
'type' => 'expense',
|
||||
'color' => '#dd4b39',
|
||||
'enabled' => '1'
|
||||
]);
|
||||
|
||||
foreach ($company->bills as $bill) {
|
||||
$bill->category_id = $bill_category->id;
|
||||
$bill->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function changeQuantityColumn()
|
||||
{
|
||||
$connection = env('DB_CONNECTION');
|
||||
|
||||
if ($connection == 'mysql') {
|
||||
$tables = [
|
||||
env('DB_PREFIX') . 'invoice_items',
|
||||
env('DB_PREFIX') . 'bill_items'
|
||||
];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
DB::statement("ALTER TABLE `$table` MODIFY `quantity` DOUBLE(7,2) NOT NULL");
|
||||
}
|
||||
} else {
|
||||
Schema::table('invoice_items', function ($table) {
|
||||
$table->decimal('quantity', 7, 2)->change();
|
||||
});
|
||||
|
||||
Schema::table('bill_items', function ($table) {
|
||||
$table->decimal('quantity', 7, 2)->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -65,6 +65,6 @@ class Permission extends LaratrustPermission
|
||||
$input = $request->input();
|
||||
$limit = $request->get('limit', setting('general.list_limit', '25'));
|
||||
|
||||
return $this->filter($input)->sortable($sort)->paginate($limit);
|
||||
return $query->filter($input)->sortable($sort)->paginate($limit);
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,6 @@ class Role extends LaratrustRole
|
||||
$input = $request->input();
|
||||
$limit = $request->get('limit', setting('general.list_limit', '25'));
|
||||
|
||||
return $this->filter($input)->sortable($sort)->paginate($limit);
|
||||
return $query->filter($input)->sortable($sort)->paginate($limit);
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ class User extends Authenticatable
|
||||
$input = $request->input();
|
||||
$limit = $request->get('limit', setting('general.list_limit', '25'));
|
||||
|
||||
return $this->filter($input)->sortable($sort)->paginate($limit);
|
||||
return $query->filter($input)->sortable($sort)->paginate($limit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
29
app/Models/Common/Recurring.php
Normal file
29
app/Models/Common/Recurring.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Common;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Traits\Recurring as RecurringTrait;
|
||||
|
||||
class Recurring extends Model
|
||||
{
|
||||
use RecurringTrait;
|
||||
|
||||
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 recurable models.
|
||||
*/
|
||||
public function recurable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
@ -106,6 +106,11 @@ class Company extends Eloquent
|
||||
return $this->hasMany('App\Models\Expense\Payment');
|
||||
}
|
||||
|
||||
public function recurring()
|
||||
{
|
||||
return $this->hasMany('App\Models\Common\Recurring');
|
||||
}
|
||||
|
||||
public function revenues()
|
||||
{
|
||||
return $this->hasMany('App\Models\Income\Revenue');
|
||||
|
@ -5,16 +5,24 @@ namespace App\Models\Expense;
|
||||
use App\Models\Model;
|
||||
use App\Traits\Currencies;
|
||||
use App\Traits\DateTime;
|
||||
use App\Traits\Media;
|
||||
use App\Traits\Recurring;
|
||||
use Bkwld\Cloner\Cloneable;
|
||||
use Sofa\Eloquence\Eloquence;
|
||||
use App\Traits\Media;
|
||||
|
||||
class Bill extends Model
|
||||
{
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Media;
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Media, Recurring;
|
||||
|
||||
protected $table = 'bills';
|
||||
|
||||
/**
|
||||
* The accessors to append to the model's array form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = ['attachment', 'discount'];
|
||||
|
||||
protected $dates = ['deleted_at', 'billed_at', 'due_at'];
|
||||
|
||||
/**
|
||||
@ -22,7 +30,7 @@ class Bill extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['company_id', 'bill_number', 'order_number', 'bill_status_code', 'billed_at', 'due_at', 'amount', 'currency_code', 'currency_rate', 'vendor_id', 'vendor_name', 'vendor_email', 'vendor_tax_number', 'vendor_phone', 'vendor_address', 'notes'];
|
||||
protected $fillable = ['company_id', 'bill_number', 'order_number', 'bill_status_code', 'billed_at', 'due_at', 'amount', 'currency_code', 'currency_rate', 'vendor_id', 'vendor_name', 'vendor_email', 'vendor_tax_number', 'vendor_phone', 'vendor_address', 'notes', 'category_id', 'parent_id'];
|
||||
|
||||
/**
|
||||
* Sortable columns.
|
||||
@ -51,11 +59,11 @@ class Bill extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cloneable_relations = ['items', 'totals'];
|
||||
public $cloneable_relations = ['items', 'recurring', 'totals'];
|
||||
|
||||
public function vendor()
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Expense\Vendor');
|
||||
return $this->belongsTo('App\Models\Setting\Category');
|
||||
}
|
||||
|
||||
public function currency()
|
||||
@ -63,9 +71,9 @@ class Bill extends Model
|
||||
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()
|
||||
@ -73,19 +81,29 @@ class Bill extends Model
|
||||
return $this->hasMany('App\Models\Expense\BillItem');
|
||||
}
|
||||
|
||||
public function totals()
|
||||
{
|
||||
return $this->hasMany('App\Models\Expense\BillTotal');
|
||||
}
|
||||
|
||||
public function payments()
|
||||
{
|
||||
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)
|
||||
@ -155,4 +173,24 @@ class Bill extends Model
|
||||
|
||||
return $this->getMedia('attachment')->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the discount percentage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscountAttribute()
|
||||
{
|
||||
$percent = 0;
|
||||
|
||||
$discount = $this->totals()->where('code', 'discount')->value('amount');
|
||||
|
||||
if ($discount) {
|
||||
$sub_total = $this->totals()->where('code', 'sub_total')->value('amount');
|
||||
|
||||
$percent = number_format((($discount * 100) / $sub_total), 0);
|
||||
}
|
||||
|
||||
return $percent;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Models\Expense;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Models\Setting\Tax;
|
||||
use App\Traits\DateTime;
|
||||
|
||||
class BillTotal extends Model
|
||||
@ -11,6 +12,13 @@ class BillTotal extends Model
|
||||
|
||||
protected $table = 'bill_totals';
|
||||
|
||||
/**
|
||||
* The accessors to append to the model's array form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = ['title'];
|
||||
|
||||
/**
|
||||
* Attributes that should be mass-assignable.
|
||||
*
|
||||
@ -33,4 +41,46 @@ class BillTotal extends Model
|
||||
{
|
||||
$this->attributes['amount'] = (double) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleAttribute()
|
||||
{
|
||||
$title = $this->name;
|
||||
|
||||
$percent = 0;
|
||||
|
||||
switch ($this->code) {
|
||||
case 'discount':
|
||||
$title = trans($title);
|
||||
$percent = $this->bill->discount;
|
||||
|
||||
break;
|
||||
case 'tax':
|
||||
$rate = Tax::where('name', $title)->value('rate');
|
||||
|
||||
if (!empty($rate)) {
|
||||
$percent = $rate;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($percent)) {
|
||||
$title .= ' (';
|
||||
|
||||
if (setting('general.percent_position', 'after') == 'after') {
|
||||
$title .= $percent . '%';
|
||||
} else {
|
||||
$title .= '%' . $percent;
|
||||
}
|
||||
|
||||
$title .= ')';
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,14 @@ use App\Models\Model;
|
||||
use App\Models\Setting\Category;
|
||||
use App\Traits\Currencies;
|
||||
use App\Traits\DateTime;
|
||||
use App\Traits\Media;
|
||||
use App\Traits\Recurring;
|
||||
use Bkwld\Cloner\Cloneable;
|
||||
use Sofa\Eloquence\Eloquence;
|
||||
use App\Traits\Media;
|
||||
|
||||
class Payment extends Model
|
||||
{
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Media;
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Media, Recurring;
|
||||
|
||||
protected $table = 'payments';
|
||||
|
||||
@ -23,7 +24,7 @@ class Payment extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['company_id', 'account_id', 'paid_at', 'amount', 'currency_code', 'currency_rate', 'vendor_id', 'description', 'category_id', 'payment_method', 'reference'];
|
||||
protected $fillable = ['company_id', 'account_id', 'paid_at', 'amount', 'currency_code', 'currency_rate', 'vendor_id', 'description', 'category_id', 'payment_method', 'reference', 'parent_id'];
|
||||
|
||||
/**
|
||||
* Sortable columns.
|
||||
@ -44,24 +45,31 @@ class Payment extends Model
|
||||
'description' ,
|
||||
];
|
||||
|
||||
/**
|
||||
* Clonable relationships.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $cloneable_relations = ['recurring'];
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Banking\Account');
|
||||
}
|
||||
|
||||
public function currency()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Setting\Currency', 'currency_code', 'code');
|
||||
}
|
||||
|
||||
public function 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()
|
||||
@ -69,6 +77,11 @@ class Payment extends Model
|
||||
return $this->hasMany('App\Models\Banking\Transfer');
|
||||
}
|
||||
|
||||
public function vendor()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Expense\Vendor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get only transfers.
|
||||
*
|
||||
|
@ -6,13 +6,14 @@ use App\Models\Model;
|
||||
use App\Traits\Currencies;
|
||||
use App\Traits\DateTime;
|
||||
use App\Traits\Incomes;
|
||||
use App\Traits\Media;
|
||||
use App\Traits\Recurring;
|
||||
use Bkwld\Cloner\Cloneable;
|
||||
use Sofa\Eloquence\Eloquence;
|
||||
use App\Traits\Media;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Incomes, Media;
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Incomes, Media, Recurring;
|
||||
|
||||
protected $table = 'invoices';
|
||||
|
||||
@ -21,7 +22,7 @@ class Invoice extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = ['attachment'];
|
||||
protected $appends = ['attachment', 'discount'];
|
||||
|
||||
protected $dates = ['deleted_at', 'invoiced_at', 'due_at'];
|
||||
|
||||
@ -30,7 +31,7 @@ class Invoice extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['company_id', 'invoice_number', 'order_number', 'invoice_status_code', 'invoiced_at', 'due_at', 'amount', 'currency_code', 'currency_rate', 'customer_id', 'customer_name', 'customer_email', 'customer_tax_number', 'customer_phone', 'customer_address', 'notes'];
|
||||
protected $fillable = ['company_id', 'invoice_number', 'order_number', 'invoice_status_code', 'invoiced_at', 'due_at', 'amount', 'currency_code', 'currency_rate', 'customer_id', 'customer_name', 'customer_email', 'customer_tax_number', 'customer_phone', 'customer_address', 'notes', 'category_id', 'parent_id'];
|
||||
|
||||
/**
|
||||
* Sortable columns.
|
||||
@ -59,11 +60,11 @@ class Invoice extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cloneable_relations = ['items', 'totals'];
|
||||
public $cloneable_relations = ['items', 'recurring', 'totals'];
|
||||
|
||||
public function customer()
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Income\Customer');
|
||||
return $this->belongsTo('App\Models\Setting\Category');
|
||||
}
|
||||
|
||||
public function currency()
|
||||
@ -71,9 +72,9 @@ class Invoice extends Model
|
||||
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()
|
||||
@ -81,9 +82,9 @@ class Invoice extends Model
|
||||
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()
|
||||
@ -91,9 +92,19 @@ class Invoice extends Model
|
||||
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)
|
||||
@ -164,4 +175,24 @@ class Invoice extends Model
|
||||
|
||||
return $this->getMedia('attachment')->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the discount percentage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscountAttribute()
|
||||
{
|
||||
$percent = 0;
|
||||
|
||||
$discount = $this->totals()->where('code', 'discount')->value('amount');
|
||||
|
||||
if ($discount) {
|
||||
$sub_total = $this->totals()->where('code', 'sub_total')->value('amount');
|
||||
|
||||
$percent = number_format((($discount * 100) / $sub_total), 0);
|
||||
}
|
||||
|
||||
return $percent;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Models\Income;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Models\Setting\Tax;
|
||||
use App\Traits\DateTime;
|
||||
|
||||
class InvoiceTotal extends Model
|
||||
@ -11,6 +12,13 @@ class InvoiceTotal extends Model
|
||||
|
||||
protected $table = 'invoice_totals';
|
||||
|
||||
/**
|
||||
* The accessors to append to the model's array form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = ['title'];
|
||||
|
||||
/**
|
||||
* Attributes that should be mass-assignable.
|
||||
*
|
||||
@ -33,4 +41,46 @@ class InvoiceTotal extends Model
|
||||
{
|
||||
$this->attributes['amount'] = (double) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleAttribute()
|
||||
{
|
||||
$title = $this->name;
|
||||
|
||||
$percent = 0;
|
||||
|
||||
switch ($this->code) {
|
||||
case 'discount':
|
||||
$title = trans($title);
|
||||
$percent = $this->invoice->discount;
|
||||
|
||||
break;
|
||||
case 'tax':
|
||||
$rate = Tax::where('name', $title)->value('rate');
|
||||
|
||||
if (!empty($rate)) {
|
||||
$percent = $rate;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($percent)) {
|
||||
$title .= ' (';
|
||||
|
||||
if (setting('general.percent_position', 'after') == 'after') {
|
||||
$title .= $percent . '%';
|
||||
} else {
|
||||
$title .= '%' . $percent;
|
||||
}
|
||||
|
||||
$title .= ')';
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,14 @@ use App\Models\Model;
|
||||
use App\Models\Setting\Category;
|
||||
use App\Traits\Currencies;
|
||||
use App\Traits\DateTime;
|
||||
use App\Traits\Media;
|
||||
use App\Traits\Recurring;
|
||||
use Bkwld\Cloner\Cloneable;
|
||||
use Sofa\Eloquence\Eloquence;
|
||||
use App\Traits\Media;
|
||||
|
||||
class Revenue extends Model
|
||||
{
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Media;
|
||||
use Cloneable, Currencies, DateTime, Eloquence, Media, Recurring;
|
||||
|
||||
protected $table = 'revenues';
|
||||
|
||||
@ -23,7 +24,7 @@ class Revenue extends Model
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['company_id', 'account_id', 'paid_at', 'amount', 'currency_code', 'currency_rate', 'customer_id', 'description', 'category_id', 'payment_method', 'reference'];
|
||||
protected $fillable = ['company_id', 'account_id', 'paid_at', 'amount', 'currency_code', 'currency_rate', 'customer_id', 'description', 'category_id', 'payment_method', 'reference', 'parent_id'];
|
||||
|
||||
/**
|
||||
* Sortable columns.
|
||||
@ -45,6 +46,13 @@ class Revenue extends Model
|
||||
'notes' => 2,
|
||||
];
|
||||
|
||||
/**
|
||||
* Clonable relationships.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $cloneable_relations = ['recurring'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
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');
|
||||
}
|
||||
|
||||
public function recurring()
|
||||
{
|
||||
return $this->morphOne('App\Models\Common\Recurring', 'recurable');
|
||||
}
|
||||
|
||||
public function transfers()
|
||||
{
|
||||
return $this->hasMany('App\Models\Banking\Transfer');
|
||||
|
@ -87,13 +87,15 @@ class Item extends Model
|
||||
return Item::all();
|
||||
}
|
||||
|
||||
$query = Item::select('id as item_id', 'name', 'sale_price', 'purchase_price', 'tax_id');
|
||||
$query = Item::select('id as item_id', 'name', 'sku', 'sale_price', 'purchase_price', 'tax_id');
|
||||
|
||||
$query->where('quantity', '>', '0');
|
||||
|
||||
foreach ($filter_data as $key => $value) {
|
||||
$query->where($key, 'LIKE', "%" . $value . "%");
|
||||
}
|
||||
$query->where(function ($query) use ($filter_data) {
|
||||
foreach ($filter_data as $key => $value) {
|
||||
$query->orWhere($key, 'LIKE', "%" . $value . "%");
|
||||
}
|
||||
});
|
||||
|
||||
return $query->get();
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class Model extends Eloquent
|
||||
$input = $request->input();
|
||||
$limit = $request->get('limit', setting('general.list_limit', '25'));
|
||||
|
||||
return $this->filter($input)->sortable($sort)->paginate($limit);
|
||||
return $query->filter($input)->sortable($sort)->paginate($limit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,9 +22,19 @@ class Category extends Model
|
||||
*/
|
||||
public $sortable = ['name', 'type', 'enabled'];
|
||||
|
||||
public function revenues()
|
||||
public function bills()
|
||||
{
|
||||
return $this->hasMany('App\Models\Income\Revenue');
|
||||
return $this->hasMany('App\Models\Expense\Bill');
|
||||
}
|
||||
|
||||
public function invoices()
|
||||
{
|
||||
return $this->hasMany('App\Models\Income\Invoice');
|
||||
}
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany('App\Models\Item\Item');
|
||||
}
|
||||
|
||||
public function payments()
|
||||
@ -32,9 +42,9 @@ class Category extends Model
|
||||
return $this->hasMany('App\Models\Expense\Payment');
|
||||
}
|
||||
|
||||
public function items()
|
||||
public function revenues()
|
||||
{
|
||||
return $this->hasMany('App\Models\Item\Item');
|
||||
return $this->hasMany('App\Models\Income\Revenue');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,13 @@ class Tax extends Model
|
||||
|
||||
protected $table = 'taxes';
|
||||
|
||||
/**
|
||||
* The accessors to append to the model's array form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $appends = ['title'];
|
||||
|
||||
/**
|
||||
* Attributes that should be mass-assignable.
|
||||
*
|
||||
@ -48,4 +55,24 @@ class Tax extends Model
|
||||
{
|
||||
$this->attributes['rate'] = (double) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name including rate.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleAttribute()
|
||||
{
|
||||
$title = $this->name . ' (';
|
||||
|
||||
if (setting('general.percent_position', 'after') == 'after') {
|
||||
$title .= $this->rate . '%';
|
||||
} else {
|
||||
$title .= '%' . $this->rate;
|
||||
}
|
||||
|
||||
$title .= ')';
|
||||
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ class MessageSelector
|
||||
case 'bo':
|
||||
case 'dz':
|
||||
case 'id':
|
||||
case 'id-ID':
|
||||
case 'ja':
|
||||
case 'jv':
|
||||
case 'ka':
|
||||
@ -126,14 +127,18 @@ class MessageSelector
|
||||
case 'th':
|
||||
case 'tr':
|
||||
case 'vi':
|
||||
case 'vi-VN':
|
||||
case 'zh':
|
||||
case 'zh-TW':
|
||||
return 0;
|
||||
break;
|
||||
case 'af':
|
||||
case 'bn':
|
||||
case 'bg':
|
||||
case 'bg-BG':
|
||||
case 'ca':
|
||||
case 'da':
|
||||
case 'da-DK':
|
||||
case 'de':
|
||||
case 'de-DE':
|
||||
case 'el':
|
||||
@ -145,6 +150,7 @@ class MessageSelector
|
||||
case 'eo':
|
||||
case 'es':
|
||||
case 'es-ES':
|
||||
case 'es-MX':
|
||||
case 'et':
|
||||
case 'eu':
|
||||
case 'fa':
|
||||
@ -167,8 +173,10 @@ class MessageSelector
|
||||
case 'mr':
|
||||
case 'nah':
|
||||
case 'nb':
|
||||
case 'nb-NO':
|
||||
case 'ne':
|
||||
case 'nl':
|
||||
case 'nl-NL':
|
||||
case 'nn':
|
||||
case 'no':
|
||||
case 'om':
|
||||
@ -179,7 +187,9 @@ class MessageSelector
|
||||
case 'pt':
|
||||
case 'so':
|
||||
case 'sq':
|
||||
case 'sq-AL':
|
||||
case 'sv':
|
||||
case 'sv-SE':
|
||||
case 'sw':
|
||||
case 'ta':
|
||||
case 'te':
|
||||
@ -207,11 +217,14 @@ class MessageSelector
|
||||
case 'be':
|
||||
case 'bs':
|
||||
case 'hr':
|
||||
case 'hr-HR':
|
||||
case 'ru':
|
||||
case 'ru-RU':
|
||||
case 'sr':
|
||||
case 'uk':
|
||||
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
|
||||
case 'cs':
|
||||
case 'cs-CZ':
|
||||
case 'sk':
|
||||
return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
|
||||
case 'ga':
|
||||
|
@ -22,6 +22,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
'App\Listeners\Updates\Version112',
|
||||
'App\Listeners\Updates\Version113',
|
||||
'App\Listeners\Updates\Version119',
|
||||
'App\Listeners\Updates\Version120',
|
||||
],
|
||||
'Illuminate\Auth\Events\Login' => [
|
||||
'App\Listeners\Auth\Login',
|
||||
|
@ -27,6 +27,10 @@ class FormServiceProvider extends ServiceProvider
|
||||
'name', 'text', 'icon', 'attributes' => ['required' => 'required'], 'value' => null, 'col' => 'col-md-6',
|
||||
]);
|
||||
|
||||
Form::component('numberGroup', 'partials.form.number_group', [
|
||||
'name', 'text', 'icon', 'attributes' => ['required' => 'required'], 'value' => null, 'col' => 'col-md-6',
|
||||
]);
|
||||
|
||||
Form::component('selectGroup', 'partials.form.select_group', [
|
||||
'name', 'text', 'icon', 'values', 'selected' => null, 'attributes' => ['required' => 'required'], 'col' => 'col-md-6',
|
||||
]);
|
||||
@ -58,6 +62,10 @@ class FormServiceProvider extends ServiceProvider
|
||||
Form::component('saveButtons', 'partials.form.save_buttons', [
|
||||
'cancel', 'col' => 'col-md-12',
|
||||
]);
|
||||
|
||||
Form::component('recurring', 'partials.form.recurring', [
|
||||
'page', 'model' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,6 +38,16 @@ class ViewComposerServiceProvider extends ServiceProvider
|
||||
View::composer(
|
||||
'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'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,6 +12,25 @@ use ZipArchive;
|
||||
trait Modules
|
||||
{
|
||||
|
||||
public function checkToken($token)
|
||||
{
|
||||
$data = [
|
||||
'form_params' => [
|
||||
'token' => $token,
|
||||
]
|
||||
];
|
||||
|
||||
$response = $this->getRemote('token/check', 'POST', $data);
|
||||
|
||||
if ($response->getStatusCode() == 200) {
|
||||
$result = json_decode($response->getBody());
|
||||
|
||||
return ($result->success) ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getModules()
|
||||
{
|
||||
$response = $this->getRemote('apps/items');
|
||||
@ -89,6 +108,17 @@ trait Modules
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSearchModules($data = [])
|
||||
{
|
||||
$response = $this->getRemote('apps/search', 'GET', $data);
|
||||
|
||||
if ($response->getStatusCode() == 200) {
|
||||
return json_decode($response->getBody())->data;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getCoreVersion()
|
||||
{
|
||||
$data['query'] = Info::all();
|
||||
@ -290,6 +320,7 @@ trait Modules
|
||||
'Authorization' => 'Bearer ' . setting('general.api_token'),
|
||||
'Accept' => 'application/json',
|
||||
'Referer' => env('APP_URL'),
|
||||
'Akaunting' => version('short'),
|
||||
];
|
||||
|
||||
$data['http_errors'] = false;
|
||||
|
152
app/Traits/Recurring.php
Normal file
152
app/Traits/Recurring.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Recurr\Rule;
|
||||
use Recurr\Transformer\ArrayTransformer;
|
||||
use Recurr\Transformer\ArrayTransformerConfig;
|
||||
|
||||
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'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
if (!$schedule = $this->schedule()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $schedule->current()->getStart();
|
||||
}
|
||||
|
||||
public function next()
|
||||
{
|
||||
if (!$schedule = $this->schedule()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$next = $schedule->next()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $next->getStart();
|
||||
}
|
||||
|
||||
public function first()
|
||||
{
|
||||
if (!$schedule = $this->schedule()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $schedule->first()->getStart();
|
||||
}
|
||||
|
||||
public function last()
|
||||
{
|
||||
if (!$schedule = $this->schedule()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $schedule->last()->getStart();
|
||||
}
|
||||
|
||||
public function schedule()
|
||||
{
|
||||
$config = new ArrayTransformerConfig();
|
||||
$config->enableLastDayOfMonthFix();
|
||||
|
||||
$transformer = new ArrayTransformer();
|
||||
$transformer->setConfig($config);
|
||||
|
||||
return $transformer->transform($this->getRule());
|
||||
}
|
||||
|
||||
public function getRule()
|
||||
{
|
||||
$rule = (new Rule())
|
||||
->setStartDate($this->getRuleStartDate())
|
||||
->setTimezone($this->getRuleTimeZone())
|
||||
->setFreq($this->getRuleFrequency())
|
||||
->setInterval($this->interval);
|
||||
|
||||
// 0 means infinite
|
||||
if ($this->count != 0) {
|
||||
$rule->setCount($this->getRuleCount());
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
|
||||
public function getRuleStartDate()
|
||||
{
|
||||
return new DateTime($this->started_at, new DateTimeZone($this->getRuleTimeZone()));
|
||||
}
|
||||
|
||||
public function getRuleTimeZone()
|
||||
{
|
||||
return setting('general.timezone');
|
||||
}
|
||||
|
||||
public function getRuleCount()
|
||||
{
|
||||
// Fix for humans
|
||||
return $this->count + 1;
|
||||
}
|
||||
|
||||
public function getRuleFrequency()
|
||||
{
|
||||
return strtoupper($this->frequency);
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@
|
||||
"bkwld/cloner": "3.2.*",
|
||||
"consoletvs/charts": "4.6.*",
|
||||
"dingo/api": "1.0.0-beta8",
|
||||
"doctrine/dbal": "2.5.*",
|
||||
"fideloper/proxy": "3.3.*",
|
||||
"guzzlehttp/guzzle": "6.3.*",
|
||||
"intervention/image": "2.3.*",
|
||||
"jenssegers/date": "3.2.*",
|
||||
"jenssegers/date": "3.3.*",
|
||||
"kyslik/column-sortable": "5.4.*",
|
||||
"laracasts/flash": "3.0.*",
|
||||
"laravel/framework": "5.4.*",
|
||||
@ -32,6 +33,7 @@
|
||||
"nwidart/laravel-modules": "1.*",
|
||||
"plank/laravel-mediable": "2.5.*",
|
||||
"santigarcor/laratrust": "4.0.*",
|
||||
"simshaun/recurr": "3.0.*",
|
||||
"sofa/eloquence": "5.4.*",
|
||||
"tucker-eric/eloquentfilter": "1.1.*"
|
||||
},
|
||||
|
4923
composer.lock
generated
4923
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -115,7 +115,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'allowed' => ['en-GB', 'ar-SA', 'cs-CZ', 'de-DE', 'es-ES', 'es-MX', 'fa-IR', 'fr-FR', 'hr-HR', 'id-ID', 'it-IT', 'nb-NO', 'nl-NL', 'pt-BR', 'ru-RU', 'sq-AL', 'sv-SE', 'tr-TR', 'vi-VN', 'zh-TW'],
|
||||
'allowed' => ['en-GB', 'ar-SA', 'bg-BG', 'cs-CZ', 'da-DK', 'de-DE', 'el-GR', 'es-ES', 'es-MX', 'fa-IR', 'fr-FR', 'he-IL', 'hr-HR', 'id-ID', 'it-IT', 'nb-NO', 'nl-NL', 'pt-BR', 'ru-RU', 'sq-AL', 'sv-SE', 'tr-TR', 'uk-UA', 'vi-VN', 'zh-TW'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -4,21 +4,21 @@ return [
|
||||
|
||||
'name' => 'Akaunting',
|
||||
|
||||
'code' => 'Import',
|
||||
'code' => 'Recurring',
|
||||
|
||||
'major' => '1',
|
||||
|
||||
'minor' => '1',
|
||||
'minor' => '2',
|
||||
|
||||
'patch' => '15',
|
||||
'patch' => '0',
|
||||
|
||||
'build' => '',
|
||||
|
||||
'status' => 'Stable',
|
||||
'status' => 'RC',
|
||||
|
||||
'date' => '13-March-2018',
|
||||
'date' => '28-April-2018',
|
||||
|
||||
'time' => '16:00',
|
||||
'time' => '19:00',
|
||||
|
||||
'zone' => 'GMT +3',
|
||||
|
||||
|
@ -45,7 +45,7 @@ class CreateBillsTable extends Migration
|
||||
$table->integer('item_id')->nullable();
|
||||
$table->string('name');
|
||||
$table->string('sku')->nullable();
|
||||
$table->integer('quantity');
|
||||
$table->double('quantity', 7, 2);
|
||||
$table->double('price', 15, 4);
|
||||
$table->double('total', 15, 4);
|
||||
$table->float('tax', 15, 4)->default('0.0000');
|
||||
|
@ -45,7 +45,7 @@ class CreateInvoicesTable extends Migration
|
||||
$table->integer('item_id')->nullable();
|
||||
$table->string('name');
|
||||
$table->string('sku')->nullable();
|
||||
$table->integer('quantity');
|
||||
$table->double('quantity', 7, 2);
|
||||
$table->double('price', 15, 4);
|
||||
$table->double('total', 15, 4);
|
||||
$table->double('tax', 15, 4)->default('0.0000');
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddCategoryColumnInvoicesBills extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('invoices', function ($table) {
|
||||
$table->integer('category_id');
|
||||
});
|
||||
|
||||
Schema::table('bills', function ($table) {
|
||||
$table->integer('category_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('invoices', function ($table) {
|
||||
$table->dropColumn('category_id');
|
||||
});
|
||||
|
||||
Schema::table('bills', function ($table) {
|
||||
$table->dropColumn('category_id');
|
||||
});
|
||||
}
|
||||
}
|
@ -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');
|
||||
}
|
||||
}
|
54
database/migrations/2018_04_30_000000_add_parent_column.php
Normal file
54
database/migrations/2018_04_30_000000_add_parent_column.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddParentColumn extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('invoices', function ($table) {
|
||||
$table->integer('parent_id')->default(0);
|
||||
});
|
||||
|
||||
Schema::table('revenues', function ($table) {
|
||||
$table->integer('parent_id')->default(0);
|
||||
});
|
||||
|
||||
Schema::table('bills', function ($table) {
|
||||
$table->integer('parent_id')->default(0);
|
||||
});
|
||||
|
||||
Schema::table('payments', function ($table) {
|
||||
$table->integer('parent_id')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('invoices', function ($table) {
|
||||
$table->dropColumn('parent_id');
|
||||
});
|
||||
|
||||
Schema::table('revenues', function ($table) {
|
||||
$table->dropColumn('parent_id');
|
||||
});
|
||||
|
||||
Schema::table('bills', function ($table) {
|
||||
$table->dropColumn('parent_id');
|
||||
});
|
||||
|
||||
Schema::table('payments', function ($table) {
|
||||
$table->dropColumn('parent_id');
|
||||
});
|
||||
}
|
||||
}
|
@ -61,6 +61,8 @@ class Roles extends Seeder
|
||||
'reports-income-summary' => 'r',
|
||||
'reports-expense-summary' => 'r',
|
||||
'reports-income-expense-summary' => 'r',
|
||||
'reports-profit-loss' => 'r',
|
||||
'reports-tax-summary' => 'r',
|
||||
],
|
||||
'manager' => [
|
||||
'admin-panel' => 'r',
|
||||
@ -87,6 +89,8 @@ class Roles extends Seeder
|
||||
'reports-income-summary' => 'r',
|
||||
'reports-expense-summary' => 'r',
|
||||
'reports-income-expense-summary' => 'r',
|
||||
'reports-profit-loss' => 'r',
|
||||
'reports-tax-summary' => 'r',
|
||||
],
|
||||
'customer' => [
|
||||
'customer-panel' => 'r',
|
||||
|
@ -30,6 +30,7 @@ class Settings extends Seeder
|
||||
'general.date_format' => 'd M Y',
|
||||
'general.date_separator' => 'space',
|
||||
'general.timezone' => 'Europe/London',
|
||||
'general.percent_position' => 'after',
|
||||
'general.invoice_number_prefix' => 'INV-',
|
||||
'general.invoice_number_digit' => '5',
|
||||
'general.invoice_number_next' => '1',
|
||||
|
53
public/css/app.css
vendored
53
public/css/app.css
vendored
@ -505,10 +505,20 @@ ul.add-new.nav.navbar-nav.pull-left {
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width : 768px) {
|
||||
.amount-space {
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.print-width {
|
||||
max-width: 1500px;
|
||||
}
|
||||
|
||||
/*
|
||||
.tooltip > .tooltip-inner {
|
||||
background-color: #6da252;
|
||||
@ -552,4 +562,45 @@ span.picture, span.attachment {
|
||||
|
||||
.form-group.col-md-6 input.fake-input.form-control{
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-outer-spin-button,
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.form-small #account_id, .form-small .select2.select2-container {
|
||||
width: 90% !important;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.form-small #currency {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.input-group-recurring {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.popover-content, .discount.box-body, .discount.box-footer {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.discount-description {
|
||||
margin-top: 6px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.user.user-menu, .user.user-menu a.dropdown-toggle {
|
||||
min-height: 50px;
|
||||
}
|
||||
|
193
public/css/bootstrap3-print-fix.css
vendored
Normal file
193
public/css/bootstrap3-print-fix.css
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
@media print {
|
||||
.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
|
||||
float: left;
|
||||
}
|
||||
.col-sm-12 {
|
||||
width: 100%;
|
||||
}
|
||||
.col-sm-11 {
|
||||
width: 91.66666667%;
|
||||
}
|
||||
.col-sm-10 {
|
||||
width: 83.33333333%;
|
||||
}
|
||||
.col-sm-9 {
|
||||
width: 75%;
|
||||
}
|
||||
.col-sm-8 {
|
||||
width: 66.66666667%;
|
||||
}
|
||||
.col-sm-7 {
|
||||
width: 58.33333333%;
|
||||
}
|
||||
.col-sm-6 {
|
||||
width: 50%;
|
||||
}
|
||||
.col-sm-5 {
|
||||
width: 41.66666667%;
|
||||
}
|
||||
.col-sm-4 {
|
||||
width: 33.33333333%;
|
||||
}
|
||||
.col-sm-3 {
|
||||
width: 25%;
|
||||
}
|
||||
.col-sm-2 {
|
||||
width: 16.66666667%;
|
||||
}
|
||||
.col-sm-1 {
|
||||
width: 8.33333333%;
|
||||
}
|
||||
.col-sm-pull-12 {
|
||||
right: 100%;
|
||||
}
|
||||
.col-sm-pull-11 {
|
||||
right: 91.66666667%;
|
||||
}
|
||||
.col-sm-pull-10 {
|
||||
right: 83.33333333%;
|
||||
}
|
||||
.col-sm-pull-9 {
|
||||
right: 75%;
|
||||
}
|
||||
.col-sm-pull-8 {
|
||||
right: 66.66666667%;
|
||||
}
|
||||
.col-sm-pull-7 {
|
||||
right: 58.33333333%;
|
||||
}
|
||||
.col-sm-pull-6 {
|
||||
right: 50%;
|
||||
}
|
||||
.col-sm-pull-5 {
|
||||
right: 41.66666667%;
|
||||
}
|
||||
.col-sm-pull-4 {
|
||||
right: 33.33333333%;
|
||||
}
|
||||
.col-sm-pull-3 {
|
||||
right: 25%;
|
||||
}
|
||||
.col-sm-pull-2 {
|
||||
right: 16.66666667%;
|
||||
}
|
||||
.col-sm-pull-1 {
|
||||
right: 8.33333333%;
|
||||
}
|
||||
.col-sm-pull-0 {
|
||||
right: auto;
|
||||
}
|
||||
.col-sm-push-12 {
|
||||
left: 100%;
|
||||
}
|
||||
.col-sm-push-11 {
|
||||
left: 91.66666667%;
|
||||
}
|
||||
.col-sm-push-10 {
|
||||
left: 83.33333333%;
|
||||
}
|
||||
.col-sm-push-9 {
|
||||
left: 75%;
|
||||
}
|
||||
.col-sm-push-8 {
|
||||
left: 66.66666667%;
|
||||
}
|
||||
.col-sm-push-7 {
|
||||
left: 58.33333333%;
|
||||
}
|
||||
.col-sm-push-6 {
|
||||
left: 50%;
|
||||
}
|
||||
.col-sm-push-5 {
|
||||
left: 41.66666667%;
|
||||
}
|
||||
.col-sm-push-4 {
|
||||
left: 33.33333333%;
|
||||
}
|
||||
.col-sm-push-3 {
|
||||
left: 25%;
|
||||
}
|
||||
.col-sm-push-2 {
|
||||
left: 16.66666667%;
|
||||
}
|
||||
.col-sm-push-1 {
|
||||
left: 8.33333333%;
|
||||
}
|
||||
.col-sm-push-0 {
|
||||
left: auto;
|
||||
}
|
||||
.col-sm-offset-12 {
|
||||
margin-left: 100%;
|
||||
}
|
||||
.col-sm-offset-11 {
|
||||
margin-left: 91.66666667%;
|
||||
}
|
||||
.col-sm-offset-10 {
|
||||
margin-left: 83.33333333%;
|
||||
}
|
||||
.col-sm-offset-9 {
|
||||
margin-left: 75%;
|
||||
}
|
||||
.col-sm-offset-8 {
|
||||
margin-left: 66.66666667%;
|
||||
}
|
||||
.col-sm-offset-7 {
|
||||
margin-left: 58.33333333%;
|
||||
}
|
||||
.col-sm-offset-6 {
|
||||
margin-left: 50%;
|
||||
}
|
||||
.col-sm-offset-5 {
|
||||
margin-left: 41.66666667%;
|
||||
}
|
||||
.col-sm-offset-4 {
|
||||
margin-left: 33.33333333%;
|
||||
}
|
||||
.col-sm-offset-3 {
|
||||
margin-left: 25%;
|
||||
}
|
||||
.col-sm-offset-2 {
|
||||
margin-left: 16.66666667%;
|
||||
}
|
||||
.col-sm-offset-1 {
|
||||
margin-left: 8.33333333%;
|
||||
}
|
||||
.col-sm-offset-0 {
|
||||
margin-left: 0%;
|
||||
}
|
||||
.visible-xs {
|
||||
display: none !important;
|
||||
}
|
||||
.hidden-xs {
|
||||
display: block !important;
|
||||
}
|
||||
table.hidden-xs {
|
||||
display: table;
|
||||
}
|
||||
tr.hidden-xs {
|
||||
display: table-row !important;
|
||||
}
|
||||
th.hidden-xs,
|
||||
td.hidden-xs {
|
||||
display: table-cell !important;
|
||||
}
|
||||
.hidden-xs.hidden-print {
|
||||
display: none !important;
|
||||
}
|
||||
.hidden-sm {
|
||||
display: none !important;
|
||||
}
|
||||
.visible-sm {
|
||||
display: block !important;
|
||||
}
|
||||
table.visible-sm {
|
||||
display: table;
|
||||
}
|
||||
tr.visible-sm {
|
||||
display: table-row !important;
|
||||
}
|
||||
th.visible-sm,
|
||||
td.visible-sm {
|
||||
display: table-cell !important;
|
||||
}
|
||||
}
|
36
public/js/app.js
vendored
36
public/js/app.js
vendored
@ -186,6 +186,11 @@ $(document).ready(function () {
|
||||
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) {
|
||||
@ -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');
|
||||
}
|
||||
});
|
||||
|
@ -12,11 +12,16 @@ return [
|
||||
'quantity' => 'الكمية',
|
||||
'price' => 'السعر',
|
||||
'sub_total' => 'المبلغ الاجمالى',
|
||||
'discount' => 'Discount',
|
||||
'tax_total' => 'اجمالى الضريبة',
|
||||
'total' => 'اجمالى',
|
||||
|
||||
'item_name' => 'اسم الصنف | اسماء الصنف',
|
||||
|
||||
'show_discount' => ':discount% Discount',
|
||||
'add_discount' => 'Add Discount',
|
||||
'discount_desc' => 'of subtotal',
|
||||
|
||||
'payment_due' => 'استحقاق الدفع',
|
||||
'amount_due' => 'استحقاق المبلغ',
|
||||
'paid' => 'مدفوع',
|
||||
|
@ -2,47 +2,49 @@
|
||||
|
||||
return [
|
||||
|
||||
'items' => 'الصنف| أصناف',
|
||||
'incomes' => 'دخل | دخل',
|
||||
'items' => 'الصنف|أصناف',
|
||||
'incomes' => 'إيراد|إيرادات',
|
||||
'invoices' => 'فاتورة|فواتير',
|
||||
'revenues' => 'الايراد | الايرادات',
|
||||
'customers' => 'العميل | العملاء',
|
||||
'expenses' => 'المصروف | المصروفات',
|
||||
'bills' => 'فاتورة شراء| فواتير شراء',
|
||||
'payments' => 'دفع | مدفوعات',
|
||||
'vendors' => 'المورد | الموردين',
|
||||
'accounts' => 'حساب | حسابات',
|
||||
'transfers' => 'تحويل | تحويلات',
|
||||
'transactions' => 'عملية | عمليات',
|
||||
'reports' => 'تقرير | تقارير',
|
||||
'settings' => 'اعداد | اعدادات',
|
||||
'categories' => 'الفئة | الفئات',
|
||||
'currencies' => 'عملة | عملات',
|
||||
'tax_rates' => 'معدل الضريبة | معدلات الضرائب',
|
||||
'users' => 'مستخدم | مستخدمين',
|
||||
'roles' => 'الوظيفة | الوظائف',
|
||||
'permissions' => 'تصريح | تصريحات',
|
||||
'modules' => 'تطبيق | تطبيقات',
|
||||
'companies' => 'شركة | شركات',
|
||||
'profits' => 'ربح | أرباح',
|
||||
'taxes' => 'ضريبة | ضرائب',
|
||||
'pictures' => 'صورة | صور',
|
||||
'types' => 'نوع | أنواع',
|
||||
'payment_methods' => 'طريقة الدفع | طرق الدفع',
|
||||
'compares' => 'الايراد مقابل المصروف | الايرادات مقابل المصروفات',
|
||||
'notes' => 'ملحوظة | ملاحظات',
|
||||
'totals' => 'اجمالى | اجماليات',
|
||||
'languages' => 'لغة | لغات',
|
||||
'updates' => 'تحديث | تحديثات',
|
||||
'numbers' => 'رقم | أرقام',
|
||||
'statuses' => 'حالة | حالات',
|
||||
'revenues' => 'الدخل|الدخل',
|
||||
'customers' => 'العميل|العملاء',
|
||||
'expenses' => 'المصروف|المصروفات',
|
||||
'bills' => 'سند | سندات الإيصال',
|
||||
'payments' => 'دفع|مدفوعات',
|
||||
'vendors' => 'المورد|الموردين',
|
||||
'accounts' => 'حساب|حسابات',
|
||||
'transfers' => 'تحويل|تحويلات',
|
||||
'transactions' => 'عملية|عمليات',
|
||||
'reports' => 'تقرير|تقارير',
|
||||
'settings' => 'اعداد|اعدادات',
|
||||
'categories' => 'الفئة|الفئات',
|
||||
'currencies' => 'عملة|عملات',
|
||||
'tax_rates' => 'معدل الضريبة|معدلات الضرائب',
|
||||
'users' => 'مستخدم|مستخدمين',
|
||||
'roles' => 'الوظيفة|الوظائف',
|
||||
'permissions' => 'تصريح|تصريحات',
|
||||
'modules' => 'تطبيق|تطبيقات',
|
||||
'companies' => 'شركة|شركات',
|
||||
'profits' => 'ربح|أرباح',
|
||||
'taxes' => 'ضريبة|ضرائب',
|
||||
'logos' => 'الشعار',
|
||||
'pictures' => 'صورة|صور',
|
||||
'types' => 'نوع|أنواع',
|
||||
'payment_methods' => 'طريقة الدفع|طرق الدفع',
|
||||
'compares' => 'الإيراد مقابل المصروف|الإيرادات مقابل المصروفات',
|
||||
'notes' => 'ملحوظة|ملاحظات',
|
||||
'totals' => 'المجموع|الإجمالي',
|
||||
'languages' => 'لغة|لغات',
|
||||
'updates' => 'تحديث|تحديثات',
|
||||
'numbers' => 'رقم|أرقام',
|
||||
'statuses' => 'حالة|حالات',
|
||||
'others' => 'Other|Others',
|
||||
|
||||
'dashboard' => 'لوحة التحكم',
|
||||
'banking' => 'الخدمات المصرفية',
|
||||
'general' => 'عام',
|
||||
'no_records' => 'لا توجد سجلات.',
|
||||
'date' => 'تاريخ',
|
||||
'amount' => 'قيمة',
|
||||
'amount' => 'المبلغ',
|
||||
'enabled' => 'مفعل',
|
||||
'disabled' => 'غير مفعل',
|
||||
'yes' => 'نعم',
|
||||
@ -52,64 +54,64 @@ return [
|
||||
'monthly' => 'شهرى',
|
||||
'quarterly' => 'ربع سنوي',
|
||||
'yearly' => 'سنوى',
|
||||
'add' => 'اضافة',
|
||||
'add_new' => 'اضافة جديد',
|
||||
'add' => 'إضافة',
|
||||
'add_new' => 'إضافة جديد',
|
||||
'show' => 'عرض',
|
||||
'edit' => 'تعديل',
|
||||
'delete' => 'حذف',
|
||||
'send' => 'ارسال',
|
||||
'download' => 'تحميل',
|
||||
'delete_confirm' => 'تأكيد الحذف : الأسم : النوع؟',
|
||||
'send' => 'إرسال',
|
||||
'download' => 'تنزيل',
|
||||
'delete_confirm' => 'تأكيد الحذف :الاسم :type؟',
|
||||
'name' => 'الاسم',
|
||||
'email' => 'البريد الالكتروني',
|
||||
'tax_number' => 'رقم الضريبة',
|
||||
'phone' => 'هاتف',
|
||||
'address' => 'عنوان',
|
||||
'address' => 'العنوان',
|
||||
'website' => 'الموقع الالكتروني',
|
||||
'actions' => 'الاجراءات',
|
||||
'actions' => 'الإجراءات',
|
||||
'description' => 'الوصف',
|
||||
'manage' => 'ادارة',
|
||||
'code' => 'كود',
|
||||
'manage' => 'إدارة',
|
||||
'code' => 'الرمز',
|
||||
'alias' => 'اسم مستعار',
|
||||
'balance' => 'رصيد',
|
||||
'balance' => 'الرصيد',
|
||||
'reference' => 'مرجع',
|
||||
'attachment' => 'مرفق',
|
||||
'change' => 'تغيير',
|
||||
'switch' => 'تبديل',
|
||||
'color' => 'لون',
|
||||
'color' => 'اللون',
|
||||
'save' => 'حفظ',
|
||||
'cancel' => 'الغاء',
|
||||
'cancel' => 'إلغاء',
|
||||
'from' => 'من',
|
||||
'to' => 'الى',
|
||||
'to' => 'إلى',
|
||||
'print' => 'طباعة',
|
||||
'search' => 'بحث',
|
||||
'search_placeholder' => 'اكتب للبحث..',
|
||||
'filter' => 'تصفية البحث',
|
||||
'help' => 'مساعدة',
|
||||
'all' => 'الكل',
|
||||
'all_type' => 'الكل : نوع',
|
||||
'upcoming' => 'المقبل',
|
||||
'created' => 'تم انشاؤه',
|
||||
'id' => 'الرقم المعرفى',
|
||||
'all_type' => 'الكل :type',
|
||||
'upcoming' => 'القادمة',
|
||||
'created' => 'تم إنشاؤه',
|
||||
'id' => 'رقم الهوية',
|
||||
'more_actions' => 'المزيد من الاجراءات',
|
||||
'duplicate' => 'تكرار',
|
||||
'unpaid' => 'Unpaid',
|
||||
'paid' => 'Paid',
|
||||
'overdue' => 'Overdue',
|
||||
'partially' => 'Partially',
|
||||
'partially_paid' => 'Partially Paid',
|
||||
'unpaid' => 'غير مدفوع',
|
||||
'paid' => 'مدفوع',
|
||||
'overdue' => 'مبلغ متأخر',
|
||||
'partially' => 'جزئي',
|
||||
'partially_paid' => 'مدفوع جزئياً',
|
||||
|
||||
'title' => [
|
||||
'new' => 'جديد : نوع',
|
||||
'edit' => 'تعديل : نوع',
|
||||
'new' => 'جديد :type',
|
||||
'edit' => 'تعديل :type',
|
||||
],
|
||||
'form' => [
|
||||
'enter' => 'ادخال : التخصص',
|
||||
'enter' => 'إدخال :field',
|
||||
'select' => [
|
||||
'field' => '-اختر : التخصص-',
|
||||
'file' => 'اختر ملف',
|
||||
'field' => '-اختر :field-',
|
||||
'file' => 'اختر ملفاً',
|
||||
],
|
||||
'no_file_selected' => 'لم تختر أي ملف...',
|
||||
'no_file_selected' => 'لم يتم اختيار أي ملف...',
|
||||
],
|
||||
|
||||
];
|
||||
|
@ -3,12 +3,12 @@
|
||||
return [
|
||||
|
||||
'change_language' => 'تغيير اللغة',
|
||||
'last_login' => 'أخر تسجيل دخول : الوقت',
|
||||
'last_login' => 'آخر تسجيل دخول :time',
|
||||
'notifications' => [
|
||||
'counter' => '{0} ليس لديك تنبيهات|{1} لديك :عدد تنبيهات|[2,*] لديك :عدد تنبيهات',
|
||||
'overdue_invoices' => '{1} :عدد الفواتير المتأخرة|[2,*] :عدد الفواتير المتأخرة',
|
||||
'upcoming_bills' => '{1} :عدد فواتير البيع الغير مدفوعة|[2,*] :عدد فواتير المشتريات الغير مدفوعة',
|
||||
'items_stock' => '{1} :عدد الأصناف الغير متوفرة بالمخزن|[2,*] :عدد الأصناف الغير متوفرة بالمخزن',
|
||||
'counter' => '{0} ليس لديك تنبيهات|{1} لديك :count تنبيهات|[2,*] لديك :count تنبيهات',
|
||||
'overdue_invoices' => '{1} :count فاتورة متأخرة|[2,*] :count فواتير متأخرة',
|
||||
'upcoming_bills' => '{1} :count فاتورة بيع قادمة|[2,*] :count فواتير بيع قادمة',
|
||||
'items_stock' => '{1} :count منتج غير متوفر|[2,*] :count منتجات غير متوفرة',
|
||||
'view_all' => 'عرض الكل'
|
||||
],
|
||||
|
||||
|
@ -12,11 +12,16 @@ return [
|
||||
'quantity' => 'الكمية',
|
||||
'price' => 'السعر',
|
||||
'sub_total' => 'المبلغ الاجمالى',
|
||||
'discount' => 'Discount',
|
||||
'tax_total' => 'اجمالى الضريبة',
|
||||
'total' => 'الاجمالى',
|
||||
|
||||
'item_name' => 'اسم الصنف | اسماء الاصناف',
|
||||
|
||||
'show_discount' => ':discount% Discount',
|
||||
'add_discount' => 'Add Discount',
|
||||
'discount_desc' => 'of subtotal',
|
||||
|
||||
'payment_due' => 'استحقاق الدفع',
|
||||
'paid' => 'مدفوع',
|
||||
'histories' => 'سجلات',
|
||||
|
@ -10,10 +10,12 @@ return [
|
||||
'imported' => ':نوع تم الاستيراد!',
|
||||
],
|
||||
'error' => [
|
||||
'payment_add' => 'خطأ: لا يمكنك إضافة الدفع! يجب عليك التحقق من إضافة المبلغ.',
|
||||
'over_payment' => 'Error: Payment not added! Amount passes the total.',
|
||||
'not_user_company' => 'خطأ: غير مسموح لك بادرة هذة الشركة!',
|
||||
'customer' => 'خطأ:غير مسموح لك باضافة مستخدم! :اسم استخدام هذا البريد الالكترونية.',
|
||||
'customer' => 'Error: User not created! :name already uses this email address.',
|
||||
'no_file' => 'خطأ: لم يتم تحديد ملف!',
|
||||
'last_category' => 'Error: Can not delete the last :type category!',
|
||||
'invalid_token' => 'Error: The token entered is invalid!',
|
||||
],
|
||||
'warning' => [
|
||||
'deleted' => 'تحذير: غير مسموح لك بحذف <b>:اسم</b> لأن هذا : مرتبط ب.',
|
||||
|
@ -8,6 +8,7 @@ return [
|
||||
'new' => 'جديد',
|
||||
'top_free' => 'المجانيات الأعلى',
|
||||
'free' => 'مجانى',
|
||||
'search' => 'Search',
|
||||
'install' => 'تثبيت',
|
||||
'buy_now' => 'اشترى الأن',
|
||||
'token_link' => '<a href="https://akaunting.com/tokens" target="_blank">اضغط هنا</a> للحصول على رمز الوصول الخاص بك API.',
|
||||
|
20
resources/lang/ar-SA/recurring.php
Normal file
20
resources/lang/ar-SA/recurring.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?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)',
|
||||
'message' => 'This is a recurring :type and the next :type will be automatically generated at :date',
|
||||
|
||||
];
|
@ -7,11 +7,24 @@ return [
|
||||
'this_quarter' => 'هذا الربع',
|
||||
'previous_quarter' => 'الربع السابق',
|
||||
'last_12_months' => 'آخر 12 شهر',
|
||||
'profit_loss' => 'Profit & Loss',
|
||||
'gross_profit' => 'Gross Profit',
|
||||
'net_profit' => 'Net Profit',
|
||||
'total_expenses' => 'Total Expenses',
|
||||
'net' => 'NET',
|
||||
|
||||
'summary' => [
|
||||
'income' => 'ملخص الايرادات',
|
||||
'expense' => 'مخلص المصروفات',
|
||||
'income_expense' => 'الإيرادات مقابل المصروفات',
|
||||
'tax' => 'Tax Summary',
|
||||
],
|
||||
|
||||
'quarter' => [
|
||||
'1' => 'Jan-Mar',
|
||||
'2' => 'Apr-Jun',
|
||||
'3' => 'Jul-Sep',
|
||||
'4' => 'Oct-Dec',
|
||||
],
|
||||
|
||||
];
|
||||
|
@ -21,6 +21,11 @@ return [
|
||||
'space' => 'مسافة ( )',
|
||||
],
|
||||
'timezone' => 'التوقيت',
|
||||
'percent' => [
|
||||
'title' => 'Percent (%) Position',
|
||||
'before' => 'Before Number',
|
||||
'after' => 'After Number',
|
||||
],
|
||||
],
|
||||
'invoice' => [
|
||||
'tab' => 'فاتورة الشراء',
|
||||
|
14
resources/lang/bg-BG/accounts.php
Normal file
14
resources/lang/bg-BG/accounts.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'account_name' => 'Oрганизация',
|
||||
'number' => 'Номер',
|
||||
'opening_balance' => 'Начално салдо',
|
||||
'current_balance' => 'Текущо салдо',
|
||||
'bank_name' => 'Име на банка',
|
||||
'bank_phone' => 'Телефон на банка',
|
||||
'bank_address' => 'Адрес на банката',
|
||||
'default_account' => 'Акаунт по подразбиране',
|
||||
|
||||
];
|
30
resources/lang/bg-BG/auth.php
Normal file
30
resources/lang/bg-BG/auth.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'profile' => 'Профил',
|
||||
'logout' => 'Изход',
|
||||
'login' => 'Вход',
|
||||
'login_to' => 'Влезте, за да стартирате сесия',
|
||||
'remember_me' => 'Запомни ме',
|
||||
'forgot_password' => 'Забравих си паролата',
|
||||
'reset_password' => 'Възстановяване на парола',
|
||||
'enter_email' => 'Въведете е-мейл адрес',
|
||||
'current_email' => 'Текущ имейл адрес',
|
||||
'reset' => 'Възстановяване',
|
||||
'never' => 'никога',
|
||||
'password' => [
|
||||
'current' => 'Парола',
|
||||
'current_confirm' => 'Потвърждение на паролата',
|
||||
'new' => 'Нова парола',
|
||||
'new_confirm' => 'Потвърждение на паролата',
|
||||
],
|
||||
'error' => [
|
||||
'self_delete' => 'Грешка: Не може да изтриете себе си!'
|
||||
],
|
||||
|
||||
'failed' => 'Неуспешно удостоверяване на потребител.',
|
||||
'disabled' => 'Този акаунт е забранен. Моля обърнете се към системния администратор.',
|
||||
'throttle' => 'Твърде много опити за логин. Моля, опитайте отново след :seconds секунди.',
|
||||
|
||||
];
|
46
resources/lang/bg-BG/bills.php
Normal file
46
resources/lang/bg-BG/bills.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'bill_number' => 'Фактура Номер',
|
||||
'bill_date' => 'Дата фактура',
|
||||
'total_price' => 'Обща цена',
|
||||
'due_date' => 'Падежна дата',
|
||||
'order_number' => 'Номер на поръчка',
|
||||
'bill_from' => 'Фактура от',
|
||||
|
||||
'quantity' => 'Количество',
|
||||
'price' => 'Цена',
|
||||
'sub_total' => 'Междинна сума',
|
||||
'discount' => 'Discount',
|
||||
'tax_total' => 'Общо данък',
|
||||
'total' => 'Общо',
|
||||
|
||||
'item_name' => 'Име на артикул | Имена на артикули',
|
||||
|
||||
'show_discount' => ':discount% Discount',
|
||||
'add_discount' => 'Add Discount',
|
||||
'discount_desc' => 'of subtotal',
|
||||
|
||||
'payment_due' => 'Дължимото плащане',
|
||||
'amount_due' => 'Дължимата сума',
|
||||
'paid' => 'Платени',
|
||||
'histories' => 'История',
|
||||
'payments' => 'Плащания',
|
||||
'add_payment' => 'Добавяне на плащане',
|
||||
'mark_received' => 'Отбелязване като получено',
|
||||
'download_pdf' => 'Изтегляне на PDF',
|
||||
'send_mail' => 'Изпращане на имейл',
|
||||
|
||||
'status' => [
|
||||
'draft' => 'Чернова',
|
||||
'received' => 'Получено',
|
||||
'partial' => 'Частичен',
|
||||
'paid' => 'Платен',
|
||||
],
|
||||
|
||||
'messages' => [
|
||||
'received' => 'Фактура, отбелязана като платена!',
|
||||
],
|
||||
|
||||
];
|
13
resources/lang/bg-BG/companies.php
Normal file
13
resources/lang/bg-BG/companies.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'domain' => 'Домейн',
|
||||
'logo' => 'Лого',
|
||||
'manage' => 'Управление на фирми',
|
||||
'all' => 'Всички фирми',
|
||||
'error' => [
|
||||
'delete_active' => 'Грешка: Невъзможно изтриване на активна компания, моля, първо я променете!',
|
||||
],
|
||||
|
||||
];
|
18
resources/lang/bg-BG/currencies.php
Normal file
18
resources/lang/bg-BG/currencies.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'code' => 'Код',
|
||||
'rate' => 'Курс',
|
||||
'default' => 'Валута по подразбиране',
|
||||
'decimal_mark' => 'Десетичен знак',
|
||||
'thousands_separator' => 'Разделител за хилядни',
|
||||
'precision' => 'Точност',
|
||||
'symbol' => [
|
||||
'symbol' => 'Символ',
|
||||
'position' => 'Символ позиция',
|
||||
'before' => 'Преди сума',
|
||||
'after' => 'След сума',
|
||||
]
|
||||
|
||||
];
|
11
resources/lang/bg-BG/customers.php
Normal file
11
resources/lang/bg-BG/customers.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'allow_login' => 'Разрешавате ли достъп?',
|
||||
'user_created' => 'Създаден потребител',
|
||||
|
||||
'error' => [
|
||||
'email' => 'Този имейл вече е бил регистриран.'
|
||||
]
|
||||
];
|
24
resources/lang/bg-BG/dashboard.php
Normal file
24
resources/lang/bg-BG/dashboard.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'total_incomes' => 'Общо приходи',
|
||||
'receivables' => 'Вземания',
|
||||
'open_invoices' => 'Отворени фактури',
|
||||
'overdue_invoices' => 'Просрочени фактури',
|
||||
'total_expenses' => 'Общо разходи',
|
||||
'payables' => 'Задължения',
|
||||
'open_bills' => 'Отворени задължения',
|
||||
'overdue_bills' => 'Просрочени задължения',
|
||||
'total_profit' => 'Обща печалба',
|
||||
'open_profit' => 'Отвори печалба',
|
||||
'overdue_profit' => 'Закъсняла печалба',
|
||||
'cash_flow' => 'Паричен поток',
|
||||
'no_profit_loss' => 'Няма печалба загуба',
|
||||
'incomes_by_category' => 'Приходи по категория',
|
||||
'expenses_by_category' => 'Разходи по категория',
|
||||
'account_balance' => 'Салдо',
|
||||
'latest_incomes' => 'Последни приходи',
|
||||
'latest_expenses' => 'Последни разходи',
|
||||
|
||||
];
|
17
resources/lang/bg-BG/demo.php
Normal file
17
resources/lang/bg-BG/demo.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'accounts_cash' => 'В брой',
|
||||
'categories_uncat' => 'Некатегоризирани',
|
||||
'categories_deposit' => 'Депозит',
|
||||
'categories_sales' => 'Продажби',
|
||||
'currencies_usd' => 'Американски долар',
|
||||
'currencies_eur' => 'Евро',
|
||||
'currencies_gbp' => 'Британска лира',
|
||||
'currencies_try' => 'Турска лира',
|
||||
'taxes_exempt' => 'Освободени от данъци',
|
||||
'taxes_normal' => 'Нормален данък',
|
||||
'taxes_sales' => 'Данък продажби',
|
||||
|
||||
];
|
9
resources/lang/bg-BG/footer.php
Normal file
9
resources/lang/bg-BG/footer.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'version' => 'Версия',
|
||||
'powered' => 'С подкрепата на Akaunting',
|
||||
'software' => 'Безплатен счетоводен софтуер',
|
||||
|
||||
];
|
117
resources/lang/bg-BG/general.php
Normal file
117
resources/lang/bg-BG/general.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'items' => 'Стока | Стоки',
|
||||
'incomes' => 'Приход | Приходи',
|
||||
'invoices' => 'Фактура | Фактури',
|
||||
'revenues' => 'Приходи | Приходи',
|
||||
'customers' => 'Клиент | Клиенти',
|
||||
'expenses' => 'Разход| Разходи',
|
||||
'bills' => 'Сметка| Сметки',
|
||||
'payments' => 'Плащане | Плащания',
|
||||
'vendors' => 'Доставчик | Доставчици',
|
||||
'accounts' => 'Сметка | Сметки',
|
||||
'transfers' => 'Трансфер | Трансфери',
|
||||
'transactions' => 'Транзакция | Транзакции',
|
||||
'reports' => 'Доклад | Доклади',
|
||||
'settings' => 'Настройка | Настройки',
|
||||
'categories' => 'Категория | Категории',
|
||||
'currencies' => 'Валута | Валути',
|
||||
'tax_rates' => 'Данък | Данъци',
|
||||
'users' => 'Потребител | Потребители',
|
||||
'roles' => 'Роля | Роли',
|
||||
'permissions' => 'Позволение | Позволения',
|
||||
'modules' => 'Добавка | Добавки',
|
||||
'companies' => 'Компания | Компании',
|
||||
'profits' => 'Печалба | Печалби',
|
||||
'taxes' => 'Данък | Данъци',
|
||||
'logos' => 'Лого | Лога',
|
||||
'pictures' => 'Снимка | Снимки',
|
||||
'types' => 'Тип | Видове',
|
||||
'payment_methods' => 'Метод на плащане | Начини на плащане',
|
||||
'compares' => 'Приход срещу разход | Приходи срещу разходи',
|
||||
'notes' => 'Бележка | Бележки',
|
||||
'totals' => 'Общо | Общи суми',
|
||||
'languages' => 'Език | Езици',
|
||||
'updates' => 'Актуализация | Актуализации',
|
||||
'numbers' => 'Номер | Числа',
|
||||
'statuses' => 'Статус | Статуси',
|
||||
'others' => 'Other|Others',
|
||||
|
||||
'dashboard' => 'Табло',
|
||||
'banking' => 'Банкиране',
|
||||
'general' => 'Общи',
|
||||
'no_records' => 'Няма записи.',
|
||||
'date' => 'Дата',
|
||||
'amount' => 'Сума',
|
||||
'enabled' => 'Включен',
|
||||
'disabled' => 'Изключен',
|
||||
'yes' => 'Да',
|
||||
'no' => 'Не',
|
||||
'na' => 'Не е достъпно',
|
||||
'daily' => 'Дневно',
|
||||
'monthly' => 'Месечно',
|
||||
'quarterly' => 'На тримесечие',
|
||||
'yearly' => 'Годишно',
|
||||
'add' => 'Добави',
|
||||
'add_new' => 'Добави нов',
|
||||
'show' => 'Покажи',
|
||||
'edit' => 'Редактиране',
|
||||
'delete' => 'Изтрий',
|
||||
'send' => 'Изпрати',
|
||||
'download' => 'Изтегли',
|
||||
'delete_confirm' => 'Потвърждаване на изтриване :name :type?',
|
||||
'name' => 'Име',
|
||||
'email' => 'Имейл',
|
||||
'tax_number' => 'Данъчен номер',
|
||||
'phone' => 'Телефон',
|
||||
'address' => 'Адрес',
|
||||
'website' => 'Сайт',
|
||||
'actions' => 'Действия',
|
||||
'description' => 'Описание',
|
||||
'manage' => 'Управление',
|
||||
'code' => 'Код',
|
||||
'alias' => 'Псевдоним',
|
||||
'balance' => 'Баланс',
|
||||
'reference' => 'Референция',
|
||||
'attachment' => 'Прикачен файл',
|
||||
'change' => 'Промени',
|
||||
'switch' => 'Превключи',
|
||||
'color' => 'Цвят',
|
||||
'save' => 'Запиши',
|
||||
'cancel' => 'Отмени',
|
||||
'from' => 'От',
|
||||
'to' => 'До',
|
||||
'print' => 'Печат',
|
||||
'search' => 'Търси',
|
||||
'search_placeholder' => 'Пиши да търсиш..',
|
||||
'filter' => 'Филтър',
|
||||
'help' => 'Помощ',
|
||||
'all' => 'Всички',
|
||||
'all_type' => 'Всички :type',
|
||||
'upcoming' => 'Приближаващ',
|
||||
'created' => 'Създаден',
|
||||
'id' => 'ID',
|
||||
'more_actions' => 'Още действия',
|
||||
'duplicate' => 'Дублиране',
|
||||
'unpaid' => 'Неплатени',
|
||||
'paid' => 'Платени',
|
||||
'overdue' => 'Прослочени',
|
||||
'partially' => 'Частичен',
|
||||
'partially_paid' => 'Частично платено',
|
||||
|
||||
'title' => [
|
||||
'new' => 'Нов :type',
|
||||
'edit' => 'Редактирай :type',
|
||||
],
|
||||
'form' => [
|
||||
'enter' => 'Въведи :field',
|
||||
'select' => [
|
||||
'field' => '- Избери :field -',
|
||||
'file' => 'Изберете файл',
|
||||
],
|
||||
'no_file_selected' => 'Не е избран файл...',
|
||||
],
|
||||
|
||||
];
|
15
resources/lang/bg-BG/header.php
Normal file
15
resources/lang/bg-BG/header.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'change_language' => 'Промяна на езика',
|
||||
'last_login' => 'Последно влизане :time',
|
||||
'notifications' => [
|
||||
'counter' => '{0} Нямате известия|{1} Имате :count ново известие|[2,*] Имате :count нови известия',
|
||||
'overdue_invoices' => '{1} :count просрочено вземане|[2,*] :count просрочени вземания',
|
||||
'upcoming_bills' => '{1} :count наближаваща фактура|[2,*] :count наближаващи фактури',
|
||||
'items_stock' => '{1} :count стока без наличност|[2,*] :count стоки без наличност',
|
||||
'view_all' => 'Вижте всички'
|
||||
],
|
||||
|
||||
];
|
9
resources/lang/bg-BG/import.php
Normal file
9
resources/lang/bg-BG/import.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'import' => 'Импортиране',
|
||||
'title' => 'Импортиране :type',
|
||||
'message' => 'Разрешени формати: CSV, XLS. Моля, <a target="_blank" href=":link"><strong>изтегли</strong></a> примерен файл.',
|
||||
|
||||
];
|
44
resources/lang/bg-BG/install.php
Normal file
44
resources/lang/bg-BG/install.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'next' => 'Следващ',
|
||||
'refresh' => 'Обновяване',
|
||||
|
||||
'steps' => [
|
||||
'requirements' => 'Моля, изпълнете следните изисквания!',
|
||||
'language' => 'Стъпка 1/3: Избор на език',
|
||||
'database' => 'Стъпка 2/3: Избор на база данни',
|
||||
'settings' => 'Стъпка 3/3: Детайли на компанията и администратора',
|
||||
],
|
||||
|
||||
'language' => [
|
||||
'select' => 'Изберете език',
|
||||
],
|
||||
|
||||
'requirements' => [
|
||||
'enabled' => ':feature трябва да бъде активирана!',
|
||||
'disabled' => ':feature трябва да бъде дезактивирана!',
|
||||
'extension' => ':extension разширението трябва да бъде заредено!',
|
||||
'directory' => ':directory директорията трябва да е с разрешение за промяна!',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'hostname' => 'Име на хост',
|
||||
'username' => 'Потребителско име',
|
||||
'password' => 'Парола',
|
||||
'name' => 'База данни',
|
||||
],
|
||||
|
||||
'settings' => [
|
||||
'company_name' => 'Име на компанията',
|
||||
'company_email' => 'Имейл на компанията',
|
||||
'admin_email' => 'Администраторски имейл',
|
||||
'admin_password' => 'Администраторска парола',
|
||||
],
|
||||
|
||||
'error' => [
|
||||
'connection' => 'Грешка: Не можа да се свърже с базата данни! Моля, уверете се, че данните са правилни.',
|
||||
],
|
||||
|
||||
];
|
55
resources/lang/bg-BG/invoices.php
Normal file
55
resources/lang/bg-BG/invoices.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'invoice_number' => 'Номер на фактура',
|
||||
'invoice_date' => 'Дата на фактура',
|
||||
'total_price' => 'Обща цена',
|
||||
'due_date' => 'Падежна дата',
|
||||
'order_number' => 'Номер на поръчка',
|
||||
'bill_to' => 'Издадена на',
|
||||
|
||||
'quantity' => 'Количество',
|
||||
'price' => 'Цена',
|
||||
'sub_total' => 'Междинна сума',
|
||||
'discount' => 'Discount',
|
||||
'tax_total' => 'Общо данък',
|
||||
'total' => 'Общо',
|
||||
|
||||
'item_name' => 'Име на артикул | Имена на артикули',
|
||||
|
||||
'show_discount' => ':discount% Discount',
|
||||
'add_discount' => 'Add Discount',
|
||||
'discount_desc' => 'of subtotal',
|
||||
|
||||
'payment_due' => 'Дължимото плащане',
|
||||
'paid' => 'Платен',
|
||||
'histories' => 'История',
|
||||
'payments' => 'Плащания',
|
||||
'add_payment' => 'Добавяне на плащане',
|
||||
'mark_paid' => 'Отбележи като платено',
|
||||
'mark_sent' => 'Маркирай като изпратено',
|
||||
'download_pdf' => 'Изтегляне на PDF',
|
||||
'send_mail' => 'Изпращане на имейл',
|
||||
|
||||
'status' => [
|
||||
'draft' => 'Чернова',
|
||||
'sent' => 'Изпратено',
|
||||
'viewed' => 'Разгледани',
|
||||
'approved' => 'Одобрени',
|
||||
'partial' => 'Частичен',
|
||||
'paid' => 'Платен',
|
||||
],
|
||||
|
||||
'messages' => [
|
||||
'email_sent' => 'И-мейла беше изпратен успешно!',
|
||||
'marked_sent' => 'Фактурата беше изпратена успешно!',
|
||||
'email_required' => 'Няма имейл адрес за този клиент!',
|
||||
],
|
||||
|
||||
'notification' => [
|
||||
'message' => 'Вие получавате този имейл, защото имате предстояща фактура за плащане на стойност :amount на :customer.',
|
||||
'button' => 'Плати сега',
|
||||
],
|
||||
|
||||
];
|
15
resources/lang/bg-BG/items.php
Normal file
15
resources/lang/bg-BG/items.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'quantities' => 'Количество | Количества',
|
||||
'sales_price' => 'Продажна цена',
|
||||
'purchase_price' => 'Покупна цена',
|
||||
'sku' => 'SKU',
|
||||
|
||||
'notification' => [
|
||||
'message' => 'Вие получавате този имейл, защото :name е на изчерпване.',
|
||||
'button' => 'Покажи сега',
|
||||
],
|
||||
|
||||
];
|
25
resources/lang/bg-BG/messages.php
Normal file
25
resources/lang/bg-BG/messages.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'success' => [
|
||||
'added' => ':type добавен!',
|
||||
'updated' => ':type променен!',
|
||||
'deleted' => ':type изтрит!',
|
||||
'duplicated' => ':type дублиран!',
|
||||
'imported' => ':type импортиран!',
|
||||
],
|
||||
'error' => [
|
||||
'over_payment' => 'Грешка: Плащането не е добавено! Сумата преминава общата сума.',
|
||||
'not_user_company' => 'Грешка: Не ви е позволено да управлявате тази компания!',
|
||||
'customer' => 'Грешка: Потребителят не е създаден! :name вече използва този имейл адрес.',
|
||||
'no_file' => 'Грешка: Няма избран файл!',
|
||||
'last_category' => 'Error: Can not delete the last :type category!',
|
||||
'invalid_token' => 'Error: The token entered is invalid!',
|
||||
],
|
||||
'warning' => [
|
||||
'deleted' => 'Предупреждение: Не ви е позволено да изтриете <b>:name</b>, защото има :text свързан.',
|
||||
'disabled' => 'Предупреждение: Не ви е позволено да деактивирате <b>:name</b>, защото има :text свързан.',
|
||||
],
|
||||
|
||||
];
|
48
resources/lang/bg-BG/modules.php
Normal file
48
resources/lang/bg-BG/modules.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'title' => 'API Token',
|
||||
'api_token' => 'Token',
|
||||
'top_paid' => 'Топ платени',
|
||||
'new' => 'Нов',
|
||||
'top_free' => 'Топ безплатни',
|
||||
'free' => 'БЕЗПЛАТНО',
|
||||
'search' => 'Search',
|
||||
'install' => 'Инсталирай',
|
||||
'buy_now' => 'Купете сега',
|
||||
'token_link' => '<a href="https://akaunting.com/tokens" target="_blank">Натиснете тук</a> за да получите Вашия API token.',
|
||||
'no_apps' => 'Все още няма приложения в тази категория.',
|
||||
'developer' => 'Вие сте разработчик? <a href="https://akaunting.com/blog/akaunting-app-store" target="_blank">Тук</a> можете да научите как да създадете приложение и да започнете да продавате днес!',
|
||||
|
||||
'about' => 'Относно',
|
||||
|
||||
'added' => 'Добавено',
|
||||
'updated' => 'Обновено',
|
||||
'compatibility' => 'Съвместимост',
|
||||
|
||||
'installed' => ':module инсталиран',
|
||||
'uninstalled' => ':module деинсталиран',
|
||||
//'updated' => ':module updated',
|
||||
'enabled' => ':module включен',
|
||||
'disabled' => ':module изключен',
|
||||
|
||||
'tab' => [
|
||||
'installation' => 'Инсталация',
|
||||
'faq' => 'ЧЗВ',
|
||||
'changelog' => 'Списък на промените',
|
||||
],
|
||||
|
||||
'installation' => [
|
||||
'header' => 'Инсталиране на приложение',
|
||||
'download' => 'Изтегляне :module файл.',
|
||||
'unzip' => 'Извличане :module файлове.',
|
||||
'install' => 'Инсталиране :module файлове.',
|
||||
],
|
||||
|
||||
'button' => [
|
||||
'uninstall' => 'Деинсталирай',
|
||||
'disable' => 'Изключи',
|
||||
'enable' => 'Активирай',
|
||||
],
|
||||
];
|
9
resources/lang/bg-BG/pagination.php
Normal file
9
resources/lang/bg-BG/pagination.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'previous' => '« Предишна',
|
||||
'next' => 'Следваща »',
|
||||
'showing' => 'Показване на :first до :last от общо :total :type',
|
||||
|
||||
];
|
22
resources/lang/bg-BG/passwords.php
Normal file
22
resources/lang/bg-BG/passwords.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are the default lines which match reasons
|
||||
| that are given by the password broker for a password update attempt
|
||||
| has failed, such as for an invalid token or invalid new password.
|
||||
|
|
||||
*/
|
||||
|
||||
'password' => 'Паролата трябва да бъде поне шест знака и да съвпада.',
|
||||
'reset' => 'Паролата е ресетната!',
|
||||
'sent' => 'Изпратено е напомняне за вашата парола!',
|
||||
'token' => 'Този токен за ресет на парола е невалиден.',
|
||||
'user' => "Потребител с такъв e-mail адрес не може да бъде открит.",
|
||||
|
||||
];
|
20
resources/lang/bg-BG/recurring.php
Normal file
20
resources/lang/bg-BG/recurring.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?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)',
|
||||
'message' => 'This is a recurring :type and the next :type will be automatically generated at :date',
|
||||
|
||||
];
|
30
resources/lang/bg-BG/reports.php
Normal file
30
resources/lang/bg-BG/reports.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'this_year' => 'Текущата година',
|
||||
'previous_year' => 'Миналата година',
|
||||
'this_quarter' => 'Текущото тримесечие',
|
||||
'previous_quarter' => 'Предходното тримесечие',
|
||||
'last_12_months' => 'Последните 12 месеца',
|
||||
'profit_loss' => 'Profit & Loss',
|
||||
'gross_profit' => 'Gross Profit',
|
||||
'net_profit' => 'Net Profit',
|
||||
'total_expenses' => 'Total Expenses',
|
||||
'net' => 'NET',
|
||||
|
||||
'summary' => [
|
||||
'income' => 'Приходи',
|
||||
'expense' => 'Разходи',
|
||||
'income_expense' => 'Приходи срещу разходи',
|
||||
'tax' => 'Tax Summary',
|
||||
],
|
||||
|
||||
'quarter' => [
|
||||
'1' => 'Jan-Mar',
|
||||
'2' => 'Apr-Jun',
|
||||
'3' => 'Jul-Sep',
|
||||
'4' => 'Oct-Dec',
|
||||
],
|
||||
|
||||
];
|
90
resources/lang/bg-BG/settings.php
Normal file
90
resources/lang/bg-BG/settings.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'company' => [
|
||||
'name' => 'Име',
|
||||
'email' => 'Имейл',
|
||||
'phone' => 'Телефон',
|
||||
'address' => 'Адрес',
|
||||
'logo' => 'Лого',
|
||||
],
|
||||
'localisation' => [
|
||||
'tab' => 'Локализиране',
|
||||
'date' => [
|
||||
'format' => 'Формат на датата',
|
||||
'separator' => 'Разделител за дата',
|
||||
'dash' => 'Тире (-)',
|
||||
'dot' => 'Точка (.)',
|
||||
'comma' => 'Запетая (,)',
|
||||
'slash' => 'Наклонена черта (/)',
|
||||
'space' => 'Празно място ( )',
|
||||
],
|
||||
'timezone' => 'Часова зона',
|
||||
'percent' => [
|
||||
'title' => 'Percent (%) Position',
|
||||
'before' => 'Before Number',
|
||||
'after' => 'After Number',
|
||||
],
|
||||
],
|
||||
'invoice' => [
|
||||
'tab' => 'Фактура',
|
||||
'prefix' => 'Префикс',
|
||||
'digit' => 'Брой цифри',
|
||||
'next' => 'Следващия номер',
|
||||
'logo' => 'Лого',
|
||||
],
|
||||
'default' => [
|
||||
'tab' => 'По подразбиране',
|
||||
'account' => 'Акаунт по подразбиране',
|
||||
'currency' => 'Валута по подразбиране',
|
||||
'tax' => 'Данъчна ставка по подразбиране',
|
||||
'payment' => 'Начин на плащане по подразбиране',
|
||||
'language' => 'Език по подразбиране',
|
||||
],
|
||||
'email' => [
|
||||
'protocol' => 'Протокол',
|
||||
'php' => 'PHP Mail',
|
||||
'smtp' => [
|
||||
'name' => 'SMTP',
|
||||
'host' => 'SMTP хост',
|
||||
'port' => 'SMTP порт',
|
||||
'username' => 'SMTP потребител',
|
||||
'password' => 'SMTP парола',
|
||||
'encryption' => 'SMTP сигурност',
|
||||
'none' => 'Няма',
|
||||
],
|
||||
'sendmail' => 'Sendmail',
|
||||
'sendmail_path' => 'Път към Sendmail',
|
||||
'log' => 'Лог имейли',
|
||||
],
|
||||
'scheduling' => [
|
||||
'tab' => 'График на дейностите',
|
||||
'send_invoice' => 'Изпрати напомняне за фактура',
|
||||
'invoice_days' => 'Изпрати след забавени дни',
|
||||
'send_bill' => 'Изпрати напомняне за фактура',
|
||||
'bill_days' => 'Изпрати преди забавени дни',
|
||||
'cron_command' => 'Грешна команда',
|
||||
'schedule_time' => 'Час за стартиране',
|
||||
],
|
||||
'appearance' => [
|
||||
'tab' => 'Външен вид',
|
||||
'theme' => 'Тема',
|
||||
'light' => 'Светла',
|
||||
'dark' => 'Тъмна',
|
||||
'list_limit' => 'Резултати на страница',
|
||||
'use_gravatar' => 'Използвай Gravatar',
|
||||
],
|
||||
'system' => [
|
||||
'tab' => 'Система',
|
||||
'session' => [
|
||||
'lifetime' => 'Сесия живот (минути)',
|
||||
'handler' => 'Управление на сесиите',
|
||||
'file' => 'Файл',
|
||||
'database' => 'База данни',
|
||||
],
|
||||
'file_size' => 'Макс размер на файл (МБ)',
|
||||
'file_types' => 'Разрешени типове файлове',
|
||||
],
|
||||
|
||||
];
|
8
resources/lang/bg-BG/taxes.php
Normal file
8
resources/lang/bg-BG/taxes.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'rate' => 'Данък',
|
||||
'rate_percent' => 'Ставка (%)',
|
||||
|
||||
];
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user