mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Expense module
This commit is contained in:
parent
d8cb1b436d
commit
3e9e28f06f
@ -111,7 +111,7 @@ class ExpenseController extends BaseController
|
|||||||
$data['proPlanPaid'] = $account['pro_plan_paid'];
|
$data['proPlanPaid'] = $account['pro_plan_paid'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return View::make('expenses.edit', $data);
|
return View::make('expenses.edit', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,26 +124,70 @@ class ExpenseController extends BaseController
|
|||||||
public function update(UpdateExpenseRequest $request)
|
public function update(UpdateExpenseRequest $request)
|
||||||
{
|
{
|
||||||
$client = $this->expenseRepo->save($request->input());
|
$client = $this->expenseRepo->save($request->input());
|
||||||
|
|
||||||
Session::flash('message', trans('texts.updated_expense'));
|
Session::flash('message', trans('texts.updated_expense'));
|
||||||
|
|
||||||
return redirect()->to('expenses');
|
return redirect()->to('expenses');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(CreateExpenseRequest $request)
|
public function store(CreateExpenseRequest $request)
|
||||||
{
|
{
|
||||||
$expense = $this->expenseRepo->save($request->input());
|
$expense = $this->expenseRepo->save($request->input());
|
||||||
|
|
||||||
Session::flash('message', trans('texts.created_expense'));
|
Session::flash('message', trans('texts.created_expense'));
|
||||||
|
|
||||||
return redirect()->to('expenses');
|
return redirect()->to('expenses');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bulk()
|
public function bulk()
|
||||||
{
|
{
|
||||||
$action = Input::get('action');
|
$action = Input::get('action');
|
||||||
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||||
$count = $this->expenseService->bulk($ids, $action);
|
|
||||||
|
switch($action)
|
||||||
|
{
|
||||||
|
case 'invoice':
|
||||||
|
$expenses = Expense::scope($ids)->get();
|
||||||
|
$clientPublicId = null;
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
// Validate that either all expenses do not have a client or if there is a client, it is the same client
|
||||||
|
foreach ($expenses as $expense)
|
||||||
|
{
|
||||||
|
if ($expense->client_id) {
|
||||||
|
if (!$clientPublicId) {
|
||||||
|
$clientPublicId = $expense->client_id;
|
||||||
|
} else if ($clientPublicId != $expense->client_id) {
|
||||||
|
Session::flash('error', trans('texts.expense_error_multiple_clients'));
|
||||||
|
return Redirect::to('expenses');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expense->invoice_id) {
|
||||||
|
Session::flash('error', trans('texts.expense_error_invoiced'));
|
||||||
|
return Redirect::to('expenses');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expense->should_be_invoiced == 0) {
|
||||||
|
Session::flash('error', trans('texts.expense_error_should_not_be_invoiced'));
|
||||||
|
return Redirect::to('expenses');
|
||||||
|
}
|
||||||
|
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
$data[] = [
|
||||||
|
'publicId' => $expense->public_id,
|
||||||
|
'description' => $expense->public_notes,
|
||||||
|
'qty' => 1,
|
||||||
|
'cost' => $expense->amount,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect::to("invoices/create/{$clientPublicId}")->with('expenses', $data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$count = $this->expenseService->bulk($ids, $action);
|
||||||
|
}
|
||||||
|
|
||||||
if ($count > 0) {
|
if ($count > 0) {
|
||||||
$message = Utils::pluralize($action.'d_expense', $count);
|
$message = Utils::pluralize($action.'d_expense', $count);
|
||||||
@ -152,7 +196,7 @@ class ExpenseController extends BaseController
|
|||||||
|
|
||||||
return Redirect::to('expenses');
|
return Redirect::to('expenses');
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getViewModel()
|
private static function getViewModel()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -172,7 +216,7 @@ class ExpenseController extends BaseController
|
|||||||
public function show($publicId)
|
public function show($publicId)
|
||||||
{
|
{
|
||||||
$expense = Expense::withTrashed()->scope($publicId)->firstOrFail();
|
$expense = Expense::withTrashed()->scope($publicId)->firstOrFail();
|
||||||
|
|
||||||
if($expense) {
|
if($expense) {
|
||||||
Utils::trackViewed($expense->getDisplayName(), 'expense');
|
Utils::trackViewed($expense->getDisplayName(), 'expense');
|
||||||
}
|
}
|
||||||
@ -191,5 +235,5 @@ class ExpenseController extends BaseController
|
|||||||
);
|
);
|
||||||
|
|
||||||
return View::make('expenses.show', $data);
|
return View::make('expenses.show', $data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,11 +119,11 @@ class InvoiceController extends BaseController
|
|||||||
Session::put('invitation_key', $invitationKey); // track current invitation
|
Session::put('invitation_key', $invitationKey); // track current invitation
|
||||||
|
|
||||||
$account->loadLocalizationSettings($client);
|
$account->loadLocalizationSettings($client);
|
||||||
|
|
||||||
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||||
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||||
$invoice->is_pro = $account->isPro();
|
$invoice->is_pro = $account->isPro();
|
||||||
|
|
||||||
if ($invoice->invoice_design_id == CUSTOM_DESIGN) {
|
if ($invoice->invoice_design_id == CUSTOM_DESIGN) {
|
||||||
$invoice->invoice_design->javascript = $account->custom_design;
|
$invoice->invoice_design->javascript = $account->custom_design;
|
||||||
} else {
|
} else {
|
||||||
@ -204,7 +204,7 @@ class InvoiceController extends BaseController
|
|||||||
->withTrashed()
|
->withTrashed()
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
$entityType = $invoice->getEntityType();
|
$entityType = $invoice->getEntityType();
|
||||||
|
|
||||||
$contactIds = DB::table('invitations')
|
$contactIds = DB::table('invitations')
|
||||||
->join('contacts', 'contacts.id', '=', 'invitations.contact_id')
|
->join('contacts', 'contacts.id', '=', 'invitations.contact_id')
|
||||||
->where('invitations.invoice_id', '=', $invoice->id)
|
->where('invitations.invoice_id', '=', $invoice->id)
|
||||||
@ -282,7 +282,7 @@ class InvoiceController extends BaseController
|
|||||||
'actions' => $actions,
|
'actions' => $actions,
|
||||||
'lastSent' => $lastSent);
|
'lastSent' => $lastSent);
|
||||||
$data = array_merge($data, self::getViewModel());
|
$data = array_merge($data, self::getViewModel());
|
||||||
|
|
||||||
if ($clone) {
|
if ($clone) {
|
||||||
$data['formIsChanged'] = true;
|
$data['formIsChanged'] = true;
|
||||||
}
|
}
|
||||||
@ -318,14 +318,14 @@ class InvoiceController extends BaseController
|
|||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
$entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE;
|
$entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE;
|
||||||
$clientId = null;
|
$clientId = null;
|
||||||
|
|
||||||
if ($clientPublicId) {
|
if ($clientPublicId) {
|
||||||
$clientId = Client::getPrivateId($clientPublicId);
|
$clientId = Client::getPrivateId($clientPublicId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoice = $account->createInvoice($entityType, $clientId);
|
$invoice = $account->createInvoice($entityType, $clientId);
|
||||||
$invoice->public_id = 0;
|
$invoice->public_id = 0;
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
||||||
'entityType' => $invoice->getEntityType(),
|
'entityType' => $invoice->getEntityType(),
|
||||||
@ -335,7 +335,7 @@ class InvoiceController extends BaseController
|
|||||||
'title' => trans('texts.new_invoice'),
|
'title' => trans('texts.new_invoice'),
|
||||||
];
|
];
|
||||||
$data = array_merge($data, self::getViewModel());
|
$data = array_merge($data, self::getViewModel());
|
||||||
|
|
||||||
return View::make('invoices.edit', $data);
|
return View::make('invoices.edit', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,6 +380,7 @@ class InvoiceController extends BaseController
|
|||||||
'recurringHelp' => $recurringHelp,
|
'recurringHelp' => $recurringHelp,
|
||||||
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
|
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
|
||||||
'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null,
|
'tasks' => Session::get('tasks') ? json_encode(Session::get('tasks')) : null,
|
||||||
|
'expenses' => Session::get('expenses') ? json_encode(Session::get('expenses')) : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -413,7 +414,7 @@ class InvoiceController extends BaseController
|
|||||||
if ($action == 'email') {
|
if ($action == 'email') {
|
||||||
return $this->emailInvoice($invoice, Input::get('pdfupload'));
|
return $this->emailInvoice($invoice, Input::get('pdfupload'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->to($invoice->getRoute());
|
return redirect()->to($invoice->getRoute());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,7 +441,7 @@ class InvoiceController extends BaseController
|
|||||||
} elseif ($action == 'email') {
|
} elseif ($action == 'email') {
|
||||||
return $this->emailInvoice($invoice, Input::get('pdfupload'));
|
return $this->emailInvoice($invoice, Input::get('pdfupload'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->to($invoice->getRoute());
|
return redirect()->to($invoice->getRoute());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<?php namespace app\Listeners;
|
<?php namespace app\Listeners;
|
||||||
|
|
||||||
use Carbon;
|
use Carbon;
|
||||||
use App\Models\Credit;
|
use App\Models\Expense;
|
||||||
use App\Events\PaymentWasDeleted;
|
use App\Events\PaymentWasDeleted;
|
||||||
|
use App\Events\InvoiceWasDeleted;
|
||||||
use App\Ninja\Repositories\ExpenseRepository;
|
use App\Ninja\Repositories\ExpenseRepository;
|
||||||
|
|
||||||
class ExpenseListener
|
class ExpenseListener
|
||||||
@ -14,4 +15,11 @@ class ExpenseListener
|
|||||||
{
|
{
|
||||||
$this->expenseRepo = $expenseRepo;
|
$this->expenseRepo = $expenseRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deletedInvoice(InvoiceWasDeleted $event)
|
||||||
|
{
|
||||||
|
// Release any tasks associated with the deleted invoice
|
||||||
|
Expense::where('invoice_id', '=', $event->invoice->id)
|
||||||
|
->update(['invoice_id' => null]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
'is_recurring' => 'boolean',
|
'is_recurring' => 'boolean',
|
||||||
'has_tasks' => 'boolean',
|
'has_tasks' => 'boolean',
|
||||||
'auto_bill' => 'boolean',
|
'auto_bill' => 'boolean',
|
||||||
|
'has_expenses' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
// used for custom invoice numbers
|
// used for custom invoice numbers
|
||||||
@ -82,7 +83,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
public function getDisplayName()
|
public function getDisplayName()
|
||||||
{
|
{
|
||||||
return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number;
|
return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function affectsBalance()
|
public function affectsBalance()
|
||||||
@ -136,7 +137,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
return ($this->amount - $this->balance);
|
return ($this->amount - $this->balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function trashed()
|
public function trashed()
|
||||||
{
|
{
|
||||||
if ($this->client && $this->client->trashed()) {
|
if ($this->client && $this->client->trashed()) {
|
||||||
@ -207,7 +208,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
|
|
||||||
$invitation->markSent($messageId);
|
$invitation->markSent($messageId);
|
||||||
|
|
||||||
// if the user marks it as sent rather than acually sending it
|
// if the user marks it as sent rather than acually sending it
|
||||||
// then we won't track it in the activity log
|
// then we won't track it in the activity log
|
||||||
if (!$notify) {
|
if (!$notify) {
|
||||||
return;
|
return;
|
||||||
@ -358,6 +359,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
'has_tasks',
|
'has_tasks',
|
||||||
'custom_text_value1',
|
'custom_text_value1',
|
||||||
'custom_text_value2',
|
'custom_text_value2',
|
||||||
|
'has_expenses',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->client->setVisible([
|
$this->client->setVisible([
|
||||||
@ -451,7 +453,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
// Fix for months with less than 31 days
|
// Fix for months with less than 31 days
|
||||||
$transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig();
|
$transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig();
|
||||||
$transformerConfig->enableLastDayOfMonthFix();
|
$transformerConfig->enableLastDayOfMonthFix();
|
||||||
|
|
||||||
$transformer = new \Recurr\Transformer\ArrayTransformer();
|
$transformer = new \Recurr\Transformer\ArrayTransformer();
|
||||||
$transformer->setConfig($transformerConfig);
|
$transformer->setConfig($transformerConfig);
|
||||||
$dates = $transformer->transform($rule);
|
$dates = $transformer->transform($rule);
|
||||||
@ -477,7 +479,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
if (count($schedule) < 2) {
|
if (count($schedule) < 2) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $schedule[1]->getStart();
|
return $schedule[1]->getStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,7 +541,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
|||||||
if (!$nextSendDate = $this->getNextSendDate()) {
|
if (!$nextSendDate = $this->getNextSendDate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->account->getDateTime() >= $nextSendDate;
|
return $this->account->getDateTime() >= $nextSendDate;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -7,6 +7,7 @@ use App\Models\InvoiceItem;
|
|||||||
use App\Models\Invitation;
|
use App\Models\Invitation;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
|
use App\Models\Expense;
|
||||||
use App\Services\PaymentService;
|
use App\Services\PaymentService;
|
||||||
use App\Ninja\Repositories\BaseRepository;
|
use App\Ninja\Repositories\BaseRepository;
|
||||||
|
|
||||||
@ -175,7 +176,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
$table->addColumn('balance', function ($model) {
|
$table->addColumn('balance', function ($model) {
|
||||||
return $model->partial > 0 ?
|
return $model->partial > 0 ?
|
||||||
trans('texts.partial_remaining', [
|
trans('texts.partial_remaining', [
|
||||||
'partial' => Utils::formatMoney($model->partial, $model->currency_id, $model->country_id),
|
'partial' => Utils::formatMoney($model->partial, $model->currency_id, $model->country_id),
|
||||||
'balance' => Utils::formatMoney($model->balance, $model->currency_id, $model->country_id)
|
'balance' => Utils::formatMoney($model->balance, $model->currency_id, $model->country_id)
|
||||||
]) :
|
]) :
|
||||||
Utils::formatMoney($model->balance, $model->currency_id, $model->country_id);
|
Utils::formatMoney($model->balance, $model->currency_id, $model->country_id);
|
||||||
@ -204,6 +205,9 @@ class InvoiceRepository extends BaseRepository
|
|||||||
if (isset($data['has_tasks']) && filter_var($data['has_tasks'], FILTER_VALIDATE_BOOLEAN)) {
|
if (isset($data['has_tasks']) && filter_var($data['has_tasks'], FILTER_VALIDATE_BOOLEAN)) {
|
||||||
$invoice->has_tasks = true;
|
$invoice->has_tasks = true;
|
||||||
}
|
}
|
||||||
|
if (isset($data['has_expenses']) && filter_var($data['has_expenses'], FILTER_VALIDATE_BOOLEAN)) {
|
||||||
|
$invoice->has_expenses = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$invoice = Invoice::scope($publicId)->firstOrFail();
|
$invoice = Invoice::scope($publicId)->firstOrFail();
|
||||||
}
|
}
|
||||||
@ -265,7 +269,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
if (isset($data['po_number'])) {
|
if (isset($data['po_number'])) {
|
||||||
$invoice->po_number = trim($data['po_number']);
|
$invoice->po_number = trim($data['po_number']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoice->invoice_design_id = isset($data['invoice_design_id']) ? $data['invoice_design_id'] : $account->invoice_design_id;
|
$invoice->invoice_design_id = isset($data['invoice_design_id']) ? $data['invoice_design_id'] : $account->invoice_design_id;
|
||||||
|
|
||||||
if (isset($data['tax_name']) && isset($data['tax_rate']) && $data['tax_name']) {
|
if (isset($data['tax_name']) && isset($data['tax_rate']) && $data['tax_name']) {
|
||||||
@ -387,6 +391,14 @@ class InvoiceRepository extends BaseRepository
|
|||||||
$task->save();
|
$task->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($item['expense_public_id']) && $item['expense_public_id']) {
|
||||||
|
$expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail();
|
||||||
|
$expense->invoice_id = $invoice->id;
|
||||||
|
$expense->invoice_client_id = $invoice->client_id;
|
||||||
|
$expense->is_invoiced = true;
|
||||||
|
$expense->save();
|
||||||
|
}
|
||||||
|
|
||||||
if ($item['product_key']) {
|
if ($item['product_key']) {
|
||||||
$productKey = trim($item['product_key']);
|
$productKey = trim($item['product_key']);
|
||||||
if (\Auth::user()->account->update_products && ! strtotime($productKey)) {
|
if (\Auth::user()->account->update_products && ! strtotime($productKey)) {
|
||||||
@ -395,7 +407,10 @@ class InvoiceRepository extends BaseRepository
|
|||||||
$product = Product::createNew();
|
$product = Product::createNew();
|
||||||
$product->product_key = trim($item['product_key']);
|
$product->product_key = trim($item['product_key']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$product->notes = $invoice->has_tasks ? '' : $item['notes'];
|
$product->notes = $invoice->has_tasks ? '' : $item['notes'];
|
||||||
|
$product->notes = $invoice->has_expenses ? '' : $item['notes'];
|
||||||
|
|
||||||
$product->cost = $item['cost'];
|
$product->cost = $item['cost'];
|
||||||
$product->save();
|
$product->save();
|
||||||
}
|
}
|
||||||
@ -639,7 +654,7 @@ class InvoiceRepository extends BaseRepository
|
|||||||
public function findNeedingReminding($account)
|
public function findNeedingReminding($account)
|
||||||
{
|
{
|
||||||
$dates = [];
|
$dates = [];
|
||||||
|
|
||||||
for ($i=1; $i<=3; $i++) {
|
for ($i=1; $i<=3; $i++) {
|
||||||
if ($date = $account->getReminderDate($i)) {
|
if ($date = $account->getReminderDate($i)) {
|
||||||
$field = $account->{"field_reminder{$i}"} == REMINDER_FIELD_DUE_DATE ? 'due_date' : 'invoice_date';
|
$field = $account->{"field_reminder{$i}"} == REMINDER_FIELD_DUE_DATE ? 'due_date' : 'invoice_date';
|
||||||
|
@ -23,7 +23,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
protected $defaultIncludes = [
|
protected $defaultIncludes = [
|
||||||
'invoice_items',
|
'invoice_items',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function includeInvoiceItems(Invoice $invoice)
|
public function includeInvoiceItems(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$transformer = new InvoiceItemTransformer($this->account, $this->serializer);
|
$transformer = new InvoiceItemTransformer($this->account, $this->serializer);
|
||||||
@ -70,6 +70,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'custom_value2' => $invoice->custom_value2,
|
'custom_value2' => $invoice->custom_value2,
|
||||||
'custom_taxes1' => (bool) $invoice->custom_taxes1,
|
'custom_taxes1' => (bool) $invoice->custom_taxes1,
|
||||||
'custom_taxes2' => (bool) $invoice->custom_taxes2,
|
'custom_taxes2' => (bool) $invoice->custom_taxes2,
|
||||||
|
'has_expenses' => (bool) $invoice->has_expenses,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class CreateExpensesTable extends Migration
|
|||||||
$table->unsignedInteger('account_id')->index();
|
$table->unsignedInteger('account_id')->index();
|
||||||
$table->unsignedInteger('vendor_id')->nullable();
|
$table->unsignedInteger('vendor_id')->nullable();
|
||||||
$table->unsignedInteger('user_id');
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->unsignedInteger('invoice_id')->nullable();
|
||||||
$table->unsignedInteger('invoice_client_id')->nullable();
|
$table->unsignedInteger('invoice_client_id')->nullable();
|
||||||
$table->boolean('is_deleted')->default(false);
|
$table->boolean('is_deleted')->default(false);
|
||||||
$table->decimal('amount', 13, 2);
|
$table->decimal('amount', 13, 2);
|
||||||
@ -38,7 +39,7 @@ class CreateExpensesTable extends Migration
|
|||||||
// Relations
|
// Relations
|
||||||
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
// Indexes
|
// Indexes
|
||||||
$table->unsignedInteger('public_id')->index();
|
$table->unsignedInteger('public_id')->index();
|
||||||
$table->unique( array('account_id','public_id') );
|
$table->unique( array('account_id','public_id') );
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddInvoicesHasExpenses extends Migration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('invoices', function(Blueprint $table)
|
||||||
|
{
|
||||||
|
$table->boolean('has_expenses')->default(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$invoices = DB::table('invoices')
|
||||||
|
->join('expenses', 'expenses.invoice_id', '=', 'invoices.id')
|
||||||
|
->selectRaw('DISTINCT invoices.id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($invoices as $invoice) {
|
||||||
|
DB::table('invoices')
|
||||||
|
->where('id', $invoice->id)
|
||||||
|
->update(['has_tasks' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('invoices', function(Blueprint $table)
|
||||||
|
{
|
||||||
|
$table->dropColumn('has_expenses');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -263,7 +263,7 @@ return array(
|
|||||||
'deleted_vendor' => 'Successfully deleted vendor',
|
'deleted_vendor' => 'Successfully deleted vendor',
|
||||||
'deleted_vendors' => 'Successfully deleted :count vendors',
|
'deleted_vendors' => 'Successfully deleted :count vendors',
|
||||||
|
|
||||||
|
|
||||||
// Emails
|
// Emails
|
||||||
'confirmation_subject' => 'Invoice Ninja Account Confirmation',
|
'confirmation_subject' => 'Invoice Ninja Account Confirmation',
|
||||||
'confirmation_header' => 'Account Confirmation',
|
'confirmation_header' => 'Account Confirmation',
|
||||||
@ -911,7 +911,7 @@ return array(
|
|||||||
'default_invoice_footer' => 'Default Invoice Footer',
|
'default_invoice_footer' => 'Default Invoice Footer',
|
||||||
'quote_footer' => 'Quote Footer',
|
'quote_footer' => 'Quote Footer',
|
||||||
'free' => 'Free',
|
'free' => 'Free',
|
||||||
|
|
||||||
'quote_is_approved' => 'This quote is approved',
|
'quote_is_approved' => 'This quote is approved',
|
||||||
'apply_credit' => 'Apply Credit',
|
'apply_credit' => 'Apply Credit',
|
||||||
'system_settings' => 'System Settings',
|
'system_settings' => 'System Settings',
|
||||||
@ -1026,7 +1026,7 @@ return array(
|
|||||||
'archive_vendor' => 'Archive vendor',
|
'archive_vendor' => 'Archive vendor',
|
||||||
'delete_vendor' => 'Delete vendor',
|
'delete_vendor' => 'Delete vendor',
|
||||||
'view_vendor' => 'View vendor',
|
'view_vendor' => 'View vendor',
|
||||||
|
|
||||||
// Expenses
|
// Expenses
|
||||||
'expense_amount' => 'Expense amount',
|
'expense_amount' => 'Expense amount',
|
||||||
'expense_balance' => 'Expense balance',
|
'expense_balance' => 'Expense balance',
|
||||||
@ -1051,11 +1051,14 @@ return array(
|
|||||||
'view' => 'View',
|
'view' => 'View',
|
||||||
'restore_expense' => 'Restore expense',
|
'restore_expense' => 'Restore expense',
|
||||||
'invoice_expense' => 'Invoice',
|
'invoice_expense' => 'Invoice',
|
||||||
|
'expense_error_multiple_clients' =>'The expenses can\'t belong to different clients',
|
||||||
|
'expense_error_invoiced' => 'Expense have already been invoiced',
|
||||||
|
'expense_error_should_not_be_invoiced' => 'Expense maked not to be invoiced',
|
||||||
|
|
||||||
// Payment terms
|
// Payment terms
|
||||||
'num_days' => 'Number of days',
|
'num_days' => 'Number of days',
|
||||||
'create_payment_term' => 'Create payment term',
|
'create_payment_term' => 'Create payment term',
|
||||||
'edit_payment_terms' => 'Edit payment term',
|
'edit_payment_terms' => 'Edit payment term',
|
||||||
'edit_payment_term' => 'Edit payment term',
|
'edit_payment_term' => 'Edit payment term',
|
||||||
'archive_payment_term' => 'Archive payment term',
|
'archive_payment_term' => 'Archive payment term',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<li>{!! link_to(($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'), trans('texts.' . ($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'))) !!}</li>
|
<li>{!! link_to(($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'), trans('texts.' . ($entityType == ENTITY_QUOTE ? 'quotes' : 'invoices'))) !!}</li>
|
||||||
<li class="active">{{ $invoice->invoice_number }}</li>
|
<li class="active">{{ $invoice->invoice_number }}</li>
|
||||||
@endif
|
@endif
|
||||||
</ol>
|
</ol>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{!! Former::open($url)
|
{!! Former::open($url)
|
||||||
@ -39,7 +39,7 @@
|
|||||||
'client' => 'required',
|
'client' => 'required',
|
||||||
'invoice_number' => 'required',
|
'invoice_number' => 'required',
|
||||||
'product_key' => 'max:255'
|
'product_key' => 'max:255'
|
||||||
)) !!}
|
)) !!}
|
||||||
|
|
||||||
@include('partials.autocomplete_fix')
|
@include('partials.autocomplete_fix')
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ trans('texts.edit_client') }}</a> |
|
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ trans('texts.edit_client') }}</a> |
|
||||||
{!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!}
|
{!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:none">
|
<div style="display:none">
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@ -69,7 +69,7 @@
|
|||||||
<div class="form-group" style="margin-bottom: 8px">
|
<div class="form-group" style="margin-bottom: 8px">
|
||||||
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
|
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
|
||||||
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, html: $root.clientLinkText"></a>
|
<a id="createClientLink" class="pointer" data-bind="click: $root.showClientForm, html: $root.clientLinkText"></a>
|
||||||
<span data-bind="visible: $root.invoice().client().public_id() > 0" style="display:none">|
|
<span data-bind="visible: $root.invoice().client().public_id() > 0" style="display:none">|
|
||||||
<a data-bind="attr: {href: '{{ url('/clients') }}/' + $root.invoice().client().public_id()}" target="_blank">{{ trans('texts.view_client') }}</a>
|
<a data-bind="attr: {href: '{{ url('/clients') }}/' + $root.invoice().client().public_id()}" target="_blank">{{ trans('texts.view_client') }}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -85,20 +85,20 @@
|
|||||||
<label class="checkbox" data-bind="attr: {for: $index() + '_check'}" onclick="refreshPDF(true)">
|
<label class="checkbox" data-bind="attr: {for: $index() + '_check'}" onclick="refreshPDF(true)">
|
||||||
<input type="hidden" value="0" data-bind="attr: {name: 'client[contacts][' + $index() + '][send_invoice]'}">
|
<input type="hidden" value="0" data-bind="attr: {name: 'client[contacts][' + $index() + '][send_invoice]'}">
|
||||||
<input type="checkbox" value="1" data-bind="checked: send_invoice, attr: {id: $index() + '_check', name: 'client[contacts][' + $index() + '][send_invoice]'}">
|
<input type="checkbox" value="1" data-bind="checked: send_invoice, attr: {id: $index() + '_check', name: 'client[contacts][' + $index() + '][send_invoice]'}">
|
||||||
<span data-bind="html: email.display"></span>
|
<span data-bind="html: email.display"></span>
|
||||||
</label>
|
</label>
|
||||||
<span data-bind="html: $data.view_as_recipient"></span>
|
<span data-bind="html: $data.view_as_recipient"></span>
|
||||||
@if (Utils::isConfirmed())
|
@if (Utils::isConfirmed())
|
||||||
<span style="vertical-align:text-top;color:red" class="fa fa-exclamation-triangle"
|
<span style="vertical-align:text-top;color:red" class="fa fa-exclamation-triangle"
|
||||||
data-bind="visible: $data.email_error, tooltip: {title: $data.email_error}"></span>
|
data-bind="visible: $data.email_error, tooltip: {title: $data.email_error}"></span>
|
||||||
<span style="vertical-align:text-top" class="glyphicon glyphicon-info-sign"
|
<span style="vertical-align:text-top" class="glyphicon glyphicon-info-sign"
|
||||||
data-bind="visible: $data.invitation_status, tooltip: {title: $data.invitation_status, html: true},
|
data-bind="visible: $data.invitation_status, tooltip: {title: $data.invitation_status, html: true},
|
||||||
style: {color: $data.hasOwnProperty('invitation_viewed') && $data.invitation_viewed() ? '#57D172':'#B1B5BA'}"></span>
|
style: {color: $data.hasOwnProperty('invitation_viewed') && $data.invitation_viewed() ? '#57D172':'#B1B5BA'}"></span>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4" id="col_2">
|
<div class="col-md-4" id="col_2">
|
||||||
<div data-bind="visible: !is_recurring()">
|
<div data-bind="visible: !is_recurring()">
|
||||||
@ -106,7 +106,7 @@
|
|||||||
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('invoice_date') !!}
|
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('invoice_date') !!}
|
||||||
{!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_due_date"))
|
{!! Former::text('due_date')->data_bind("datePicker: due_date, valueUpdate: 'afterkeydown'")->label(trans("texts.{$entityType}_due_date"))
|
||||||
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('due_date') !!}
|
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))->appendIcon('calendar')->addGroupClass('due_date') !!}
|
||||||
|
|
||||||
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()')
|
{!! Former::text('partial')->data_bind("value: partial, valueUpdate: 'afterkeydown'")->onchange('onPartialChange()')
|
||||||
->rel('tooltip')->data_toggle('tooltip')->data_placement('bottom')->title(trans('texts.partial_value')) !!}
|
->rel('tooltip')->data_toggle('tooltip')->data_placement('bottom')->title(trans('texts.partial_value')) !!}
|
||||||
</div>
|
</div>
|
||||||
@ -127,7 +127,7 @@
|
|||||||
|
|
||||||
@if ($entityType == ENTITY_INVOICE)
|
@if ($entityType == ENTITY_INVOICE)
|
||||||
<div class="form-group" style="margin-bottom: 8px">
|
<div class="form-group" style="margin-bottom: 8px">
|
||||||
<div class="col-lg-8 col-sm-8 col-sm-offset-4" style="padding-top: 10px">
|
<div class="col-lg-8 col-sm-8 col-sm-offset-4" style="padding-top: 10px">
|
||||||
@if ($invoice->recurring_invoice)
|
@if ($invoice->recurring_invoice)
|
||||||
{!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!}
|
{!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!}
|
||||||
@elseif ($invoice->id)
|
@elseif ($invoice->id)
|
||||||
@ -151,7 +151,7 @@
|
|||||||
{!! Former::text('invoice_number')
|
{!! Former::text('invoice_number')
|
||||||
->label(trans("texts.{$entityType}_number_short"))
|
->label(trans("texts.{$entityType}_number_short"))
|
||||||
->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
|
->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
|
||||||
</span>
|
</span>
|
||||||
<span data-bind="visible: is_recurring()" style="display: none">
|
<span data-bind="visible: is_recurring()" style="display: none">
|
||||||
{!! Former::checkbox('auto_bill')
|
{!! Former::checkbox('auto_bill')
|
||||||
->label(trans('texts.auto_bill'))
|
->label(trans('texts.auto_bill'))
|
||||||
@ -163,7 +163,7 @@
|
|||||||
->addGroupClass('discount-group')->type('number')->min('0')->step('any')->append(
|
->addGroupClass('discount-group')->type('number')->min('0')->step('any')->append(
|
||||||
Former::select('is_amount_discount')->addOption(trans('texts.discount_percent'), '0')
|
Former::select('is_amount_discount')->addOption(trans('texts.discount_percent'), '0')
|
||||||
->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw()
|
->addOption(trans('texts.discount_amount'), '1')->data_bind("value: is_amount_discount")->raw()
|
||||||
) !!}
|
) !!}
|
||||||
|
|
||||||
@if ($account->showCustomField('custom_invoice_text_label2', $invoice))
|
@if ($account->showCustomField('custom_invoice_text_label2', $invoice))
|
||||||
{!! Former::text('custom_text_value2')->label($account->custom_invoice_text_label2)->data_bind("value: custom_text_value2, valueUpdate: 'afterkeydown'") !!}
|
{!! Former::text('custom_text_value2')->label($account->custom_invoice_text_label2)->data_bind("value: custom_text_value2, valueUpdate: 'afterkeydown'") !!}
|
||||||
@ -200,16 +200,17 @@
|
|||||||
!!}
|
!!}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][notes]'}"
|
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][notes]'}"
|
||||||
rows="1" cols="60" style="resize: vertical" class="form-control word-wrap"></textarea>
|
rows="1" cols="60" style="resize: vertical" class="form-control word-wrap"></textarea>
|
||||||
<input type="text" data-bind="value: task_public_id, attr: {name: 'invoice_items[' + $index() + '][task_public_id]'}" style="display: none"/>
|
<input type="text" data-bind="value: task_public_id, attr: {name: 'invoice_items[' + $index() + '][task_public_id]'}" style="display: none"/>
|
||||||
|
<input type="text" data-bind="value: expense_public_id, attr: {name: 'invoice_items[' + $index() + '][expense_public_id]'}" style="display: none"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input data-bind="value: prettyCost, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][cost]'}"
|
<input data-bind="value: prettyCost, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][cost]'}"
|
||||||
style="text-align: right" class="form-control invoice-item"/>
|
style="text-align: right" class="form-control invoice-item"/>
|
||||||
</td>
|
</td>
|
||||||
<td style="{{ $account->hide_quantity ? 'display:none' : '' }}">
|
<td style="{{ $account->hide_quantity ? 'display:none' : '' }}">
|
||||||
<input data-bind="value: prettyQty, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][qty]'}"
|
<input data-bind="value: prettyQty, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][qty]'}"
|
||||||
style="text-align: right" class="form-control invoice-item" name="quantity"/>
|
style="text-align: right" class="form-control invoice-item" name="quantity"/>
|
||||||
</td>
|
</td>
|
||||||
<td style="display:none;" data-bind="visible: $root.invoice_item_taxes.show">
|
<td style="display:none;" data-bind="visible: $root.invoice_item_taxes.show">
|
||||||
@ -221,7 +222,7 @@
|
|||||||
<div class="line-total" data-bind="text: totals.total"></div>
|
<div class="line-total" data-bind="text: totals.total"></div>
|
||||||
</td>
|
</td>
|
||||||
<td style="cursor:pointer" class="hide-border td-icon">
|
<td style="cursor:pointer" class="hide-border td-icon">
|
||||||
<i style="padding-left:2px" data-bind="click: $parent.removeItem, visible: actionsVisible() &&
|
<i style="padding-left:2px" data-bind="click: $parent.removeItem, visible: actionsVisible() &&
|
||||||
$index() < ($parent.invoice_items().length - 1) &&
|
$index() < ($parent.invoice_items().length - 1) &&
|
||||||
$parent.invoice_items().length > 1" class="fa fa-minus-circle redlink" title="Remove item"/>
|
$parent.invoice_items().length > 1" class="fa fa-minus-circle redlink" title="Remove item"/>
|
||||||
</td>
|
</td>
|
||||||
@ -245,7 +246,7 @@
|
|||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane active" id="notes" style="padding-bottom:44px">
|
<div role="tabpanel" class="tab-pane active" id="notes" style="padding-bottom:44px">
|
||||||
{!! Former::textarea('public_notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'")
|
{!! Former::textarea('public_notes')->data_bind("value: wrapped_notes, valueUpdate: 'afterkeydown'")
|
||||||
->label(null)->style('resize: none; min-width: 450px;')->rows(3) !!}
|
->label(null)->style('resize: none; min-width: 450px;')->rows(3) !!}
|
||||||
</div>
|
</div>
|
||||||
<div role="tabpanel" class="tab-pane" id="terms">
|
<div role="tabpanel" class="tab-pane" id="terms">
|
||||||
{!! Former::textarea('terms')->data_bind("value:wrapped_terms, placeholder: terms_placeholder, valueUpdate: 'afterkeydown'")
|
{!! Former::textarea('terms')->data_bind("value:wrapped_terms, placeholder: terms_placeholder, valueUpdate: 'afterkeydown'")
|
||||||
@ -316,7 +317,7 @@
|
|||||||
|
|
||||||
<tr style="display:none" data-bind="visible: $root.invoice_taxes.show">
|
<tr style="display:none" data-bind="visible: $root.invoice_taxes.show">
|
||||||
<td class="hide-border" colspan="3"/>
|
<td class="hide-border" colspan="3"/>
|
||||||
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
|
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
|
||||||
@if (!$account->hide_quantity)
|
@if (!$account->hide_quantity)
|
||||||
<td>{{ trans('texts.tax') }}</td>
|
<td>{{ trans('texts.tax') }}</td>
|
||||||
@endif
|
@endif
|
||||||
@ -369,7 +370,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
|
|
||||||
@ -383,18 +384,19 @@
|
|||||||
{!! Former::text('is_quote')->data_bind('value: is_quote') !!}
|
{!! Former::text('is_quote')->data_bind('value: is_quote') !!}
|
||||||
{!! Former::text('has_tasks')->data_bind('value: has_tasks') !!}
|
{!! Former::text('has_tasks')->data_bind('value: has_tasks') !!}
|
||||||
{!! Former::text('data')->data_bind('value: ko.mapping.toJSON(model)') !!}
|
{!! Former::text('data')->data_bind('value: ko.mapping.toJSON(model)') !!}
|
||||||
|
{!! Former::text('has_expenses')->data_bind('value: has_expenses') !!}
|
||||||
{!! Former::text('pdfupload') !!}
|
{!! Former::text('pdfupload') !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
|
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
|
||||||
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!}
|
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!}
|
||||||
@else
|
@else
|
||||||
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!}
|
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!}
|
{!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!}
|
||||||
|
|
||||||
@if ($invoice->isClientTrashed())
|
@if ($invoice->isClientTrashed())
|
||||||
<!-- do nothing -->
|
<!-- do nothing -->
|
||||||
@elseif ($invoice->trashed())
|
@elseif ($invoice->trashed())
|
||||||
@ -448,7 +450,7 @@
|
|||||||
{!! Former::text('client[vat_number]')
|
{!! Former::text('client[vat_number]')
|
||||||
->label('vat_number')
|
->label('vat_number')
|
||||||
->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!}
|
->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!}
|
||||||
|
|
||||||
{!! Former::text('client[website]')
|
{!! Former::text('client[website]')
|
||||||
->label('website')
|
->label('website')
|
||||||
->data_bind("value: website, valueUpdate: 'afterkeydown'") !!}
|
->data_bind("value: website, valueUpdate: 'afterkeydown'") !!}
|
||||||
@ -457,7 +459,7 @@
|
|||||||
->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!}
|
->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@if (Auth::user()->isPro())
|
@if (Auth::user()->isPro())
|
||||||
@if ($account->custom_client_label1)
|
@if ($account->custom_client_label1)
|
||||||
{!! Former::text('client[custom_value1]')
|
{!! Former::text('client[custom_value1]')
|
||||||
->label($account->custom_client_label1)
|
->label($account->custom_client_label1)
|
||||||
@ -503,11 +505,11 @@
|
|||||||
|
|
||||||
{!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown',
|
{!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown',
|
||||||
attr: {name: 'client[contacts][' + \$index() + '][public_id]'}") !!}
|
attr: {name: 'client[contacts][' + \$index() + '][public_id]'}") !!}
|
||||||
{!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown',
|
{!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown',
|
||||||
attr: {name: 'client[contacts][' + \$index() + '][first_name]'}") !!}
|
attr: {name: 'client[contacts][' + \$index() + '][first_name]'}") !!}
|
||||||
{!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown',
|
{!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown',
|
||||||
attr: {name: 'client[contacts][' + \$index() + '][last_name]'}") !!}
|
attr: {name: 'client[contacts][' + \$index() + '][last_name]'}") !!}
|
||||||
{!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown',
|
{!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown',
|
||||||
attr: {name: 'client[contacts][' + \$index() + '][email]', id:'email'+\$index()}")
|
attr: {name: 'client[contacts][' + \$index() + '][email]', id:'email'+\$index()}")
|
||||||
->addClass('client-email') !!}
|
->addClass('client-email') !!}
|
||||||
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown',
|
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown',
|
||||||
@ -517,7 +519,7 @@
|
|||||||
<div class="col-lg-8 col-lg-offset-4">
|
<div class="col-lg-8 col-lg-offset-4">
|
||||||
<span class="redlink bold" data-bind="visible: $parent.contacts().length > 1">
|
<span class="redlink bold" data-bind="visible: $parent.contacts().length > 1">
|
||||||
{!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!}
|
{!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!}
|
||||||
</span>
|
</span>
|
||||||
<span data-bind="visible: $index() === ($parent.contacts().length - 1)" class="pull-right greenlink bold">
|
<span data-bind="visible: $index() === ($parent.contacts().length - 1)" class="pull-right greenlink bold">
|
||||||
{!! link_to('#', trans('texts.add_contact').' +', array('data-bind'=>'click: $parent.addContact')) !!}
|
{!! link_to('#', trans('texts.add_contact').' +', array('data-bind'=>'click: $parent.addContact')) !!}
|
||||||
</span>
|
</span>
|
||||||
@ -540,7 +542,7 @@
|
|||||||
->placeholder($account->language ? $account->language->name : '')
|
->placeholder($account->language ? $account->language->name : '')
|
||||||
->label(trans('texts.language_id'))
|
->label(trans('texts.language_id'))
|
||||||
->data_bind('value: language_id')
|
->data_bind('value: language_id')
|
||||||
->fromQuery($languages, 'name', 'id') !!}
|
->fromQuery($languages, 'name', 'id') !!}
|
||||||
{!! Former::select('client[payment_terms]')->addOption('','')->data_bind('value: payment_terms')
|
{!! Former::select('client[payment_terms]')->addOption('','')->data_bind('value: payment_terms')
|
||||||
->fromQuery($paymentTerms, 'name', 'num_days')
|
->fromQuery($paymentTerms, 'name', 'num_days')
|
||||||
->label(trans('texts.payment_terms'))
|
->label(trans('texts.payment_terms'))
|
||||||
@ -565,9 +567,9 @@
|
|||||||
<span class="error-block" id="emailError" style="display:none;float:left;font-weight:bold">{{ trans('texts.provide_name_or_email') }}</span><span> </span>
|
<span class="error-block" id="emailError" style="display:none;float:left;font-weight:bold">{{ trans('texts.provide_name_or_email') }}</span><span> </span>
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
|
||||||
<button type="button" class="btn btn-default" data-bind="click: $root.showMoreFields, text: $root.showMore() ? '{{ trans('texts.less_fields') }}' : '{{ trans('texts.more_fields') }}'"></button>
|
<button type="button" class="btn btn-default" data-bind="click: $root.showMoreFields, text: $root.showMore() ? '{{ trans('texts.less_fields') }}' : '{{ trans('texts.more_fields') }}'"></button>
|
||||||
<button id="clientDoneButton" type="button" class="btn btn-primary" data-bind="click: $root.clientFormComplete">{{ trans('texts.done') }}</button>
|
<button id="clientDoneButton" type="button" class="btn btn-primary" data-bind="click: $root.clientFormComplete">{{ trans('texts.done') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -587,7 +589,7 @@
|
|||||||
<div class="modal-footer" style="margin-top: 0px">
|
<div class="modal-footer" style="margin-top: 0px">
|
||||||
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
|
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -607,9 +609,9 @@
|
|||||||
@include('invoices.knockout')
|
@include('invoices.knockout')
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
var products = {!! $products !!};
|
var products = {!! $products !!};
|
||||||
var clients = {!! $clients !!};
|
var clients = {!! $clients !!};
|
||||||
var account = {!! Auth::user()->account !!};
|
var account = {!! Auth::user()->account !!};
|
||||||
|
|
||||||
var clientMap = {};
|
var clientMap = {};
|
||||||
@ -628,17 +630,17 @@
|
|||||||
contact.send_invoice = true;
|
contact.send_invoice = true;
|
||||||
}
|
}
|
||||||
if (clientName != contactName) {
|
if (clientName != contactName) {
|
||||||
$clientSelect.append(new Option(contactName, client.public_id));
|
$clientSelect.append(new Option(contactName, client.public_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clientMap[client.public_id] = client;
|
clientMap[client.public_id] = client;
|
||||||
$clientSelect.append(new Option(clientName, client.public_id));
|
$clientSelect.append(new Option(clientName, client.public_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@if ($data)
|
@if ($data)
|
||||||
// this means we failed so we'll reload the previous state
|
// this means we failed so we'll reload the previous state
|
||||||
window.model = new ViewModel({!! $data !!});
|
window.model = new ViewModel({!! $data !!});
|
||||||
@else
|
@else
|
||||||
// otherwise create blank model
|
// otherwise create blank model
|
||||||
window.model = new ViewModel();
|
window.model = new ViewModel();
|
||||||
|
|
||||||
@ -674,7 +676,7 @@
|
|||||||
// move the blank invoice line item to the end
|
// move the blank invoice line item to the end
|
||||||
var blank = model.invoice().invoice_items.pop();
|
var blank = model.invoice().invoice_items.pop();
|
||||||
var tasks = {!! $tasks !!};
|
var tasks = {!! $tasks !!};
|
||||||
|
|
||||||
for (var i=0; i<tasks.length; i++) {
|
for (var i=0; i<tasks.length; i++) {
|
||||||
var task = tasks[i];
|
var task = tasks[i];
|
||||||
var item = model.invoice().addItem();
|
var item = model.invoice().addItem();
|
||||||
@ -685,6 +687,24 @@
|
|||||||
model.invoice().invoice_items.push(blank);
|
model.invoice().invoice_items.push(blank);
|
||||||
model.invoice().has_tasks(true);
|
model.invoice().has_tasks(true);
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if (isset($expenses) && $expenses)
|
||||||
|
// move the blank invoice line item to the end
|
||||||
|
var blank = model.invoice().invoice_items.pop();
|
||||||
|
var expenses = {!! $expenses !!};
|
||||||
|
|
||||||
|
for (var i=0; i<expenses.length; i++) {
|
||||||
|
var expense = expenses[i];
|
||||||
|
var item = model.invoice().addItem();
|
||||||
|
item.notes(expense.description);
|
||||||
|
item.qty(expense.qty);
|
||||||
|
item.expense_public_id(expense.publicId);
|
||||||
|
item.cost(expense.cost);
|
||||||
|
}
|
||||||
|
model.invoice().invoice_items.push(blank);
|
||||||
|
model.invoice().has_expenses(true);
|
||||||
|
@endif
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
model.invoice().tax(model.getTaxRate(model.invoice().tax_name(), model.invoice().tax_rate()));
|
model.invoice().tax(model.getTaxRate(model.invoice().tax_name(), model.invoice().tax_rate()));
|
||||||
@ -702,7 +722,7 @@
|
|||||||
|
|
||||||
ko.applyBindings(model);
|
ko.applyBindings(model);
|
||||||
onItemChange();
|
onItemChange();
|
||||||
|
|
||||||
|
|
||||||
$('#country_id').combobox().on('change', function(e) {
|
$('#country_id').combobox().on('change', function(e) {
|
||||||
var countryId = $('input[name=country_id]').val();
|
var countryId = $('input[name=country_id]').val();
|
||||||
@ -723,15 +743,15 @@
|
|||||||
@if ($invoice->client && !$invoice->id)
|
@if ($invoice->client && !$invoice->id)
|
||||||
$('input[name=client]').val({{ $invoice->client->public_id }});
|
$('input[name=client]').val({{ $invoice->client->public_id }});
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
var $input = $('select#client');
|
var $input = $('select#client');
|
||||||
$input.combobox().on('change', function(e) {
|
$input.combobox().on('change', function(e) {
|
||||||
var oldId = model.invoice().client().public_id();
|
var oldId = model.invoice().client().public_id();
|
||||||
var clientId = parseInt($('input[name=client]').val(), 10) || 0;
|
var clientId = parseInt($('input[name=client]').val(), 10) || 0;
|
||||||
if (clientId > 0) {
|
if (clientId > 0) {
|
||||||
var selected = clientMap[clientId];
|
var selected = clientMap[clientId];
|
||||||
model.loadClient(selected);
|
model.loadClient(selected);
|
||||||
// we enable searching by contact but the selection must be the client
|
// we enable searching by contact but the selection must be the client
|
||||||
$('.client-input').val(getClientDisplayName(selected));
|
$('.client-input').val(getClientDisplayName(selected));
|
||||||
// if there's an invoice number pattern we'll apply it now
|
// if there's an invoice number pattern we'll apply it now
|
||||||
setInvoiceNumber(selected);
|
setInvoiceNumber(selected);
|
||||||
@ -748,7 +768,7 @@
|
|||||||
$('.client_select input.form-control').on('click', function() {
|
$('.client_select input.form-control').on('click', function() {
|
||||||
model.showClientForm();
|
model.showClientForm();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#invoice_footer, #terms, #public_notes, #invoice_number, #invoice_date, #due_date, #start_date, #po_number, #discount, #currency_id, #invoice_design_id, #recurring, #is_amount_discount, #partial, #custom_text_value1, #custom_text_value2').change(function() {
|
$('#invoice_footer, #terms, #public_notes, #invoice_number, #invoice_date, #due_date, #start_date, #po_number, #discount, #currency_id, #invoice_design_id, #recurring, #is_amount_discount, #partial, #custom_text_value1, #custom_text_value2').change(function() {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -766,7 +786,7 @@
|
|||||||
(function (_field) {
|
(function (_field) {
|
||||||
$('.' + _field + ' .input-group-addon').click(function() {
|
$('.' + _field + ' .input-group-addon').click(function() {
|
||||||
toggleDatePicker(_field);
|
toggleDatePicker(_field);
|
||||||
});
|
});
|
||||||
})(field);
|
})(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,7 +795,7 @@
|
|||||||
@else
|
@else
|
||||||
$('.client_select input.form-control').focus();
|
$('.client_select input.form-control').focus();
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
$('#clientModal').on('shown.bs.modal', function () {
|
$('#clientModal').on('shown.bs.modal', function () {
|
||||||
$('#client\\[name\\]').focus();
|
$('#client\\[name\\]').focus();
|
||||||
}).on('hidden.bs.modal', function () {
|
}).on('hidden.bs.modal', function () {
|
||||||
@ -784,30 +804,34 @@
|
|||||||
refreshPDF(true);
|
refreshPDF(true);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#relatedActions > button:first').click(function() {
|
$('#relatedActions > button:first').click(function() {
|
||||||
onPaymentClick();
|
onPaymentClick();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('label.radio').addClass('radio-inline');
|
$('label.radio').addClass('radio-inline');
|
||||||
|
|
||||||
@if ($invoice->client->id)
|
@if ($invoice->client->id)
|
||||||
$input.trigger('change');
|
$input.trigger('change');
|
||||||
@else
|
@else
|
||||||
refreshPDF(true);
|
refreshPDF(true);
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
var client = model.invoice().client();
|
var client = model.invoice().client();
|
||||||
setComboboxValue($('.client_select'),
|
setComboboxValue($('.client_select'),
|
||||||
client.public_id(),
|
client.public_id(),
|
||||||
client.name.display());
|
client.name.display());
|
||||||
|
|
||||||
@if (isset($tasks) && $tasks)
|
@if (isset($tasks) && $tasks)
|
||||||
NINJA.formIsChanged = true;
|
NINJA.formIsChanged = true;
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if (isset($expenses) && $expenses)
|
||||||
|
NINJA.formIsChanged = true;
|
||||||
|
@endif
|
||||||
|
|
||||||
applyComboboxListeners();
|
applyComboboxListeners();
|
||||||
});
|
});
|
||||||
|
|
||||||
function applyComboboxListeners() {
|
function applyComboboxListeners() {
|
||||||
var selectorStr = '.invoice-table input, .invoice-table textarea';
|
var selectorStr = '.invoice-table input, .invoice-table textarea';
|
||||||
@ -980,7 +1004,7 @@
|
|||||||
var invoice = createInvoiceModel();
|
var invoice = createInvoiceModel();
|
||||||
var design = getDesignJavascript();
|
var design = getDesignJavascript();
|
||||||
if (!design) return;
|
if (!design) return;
|
||||||
|
|
||||||
doc = generatePDF(invoice, design, true);
|
doc = generatePDF(invoice, design, true);
|
||||||
doc.getDataUrl( function(pdfString){
|
doc.getDataUrl( function(pdfString){
|
||||||
$('#pdfupload').val(pdfString);
|
$('#pdfupload').val(pdfString);
|
||||||
@ -1016,7 +1040,7 @@
|
|||||||
}
|
}
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmailValid() {
|
function isEmailValid() {
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
var sendTo = false;
|
var sendTo = false;
|
||||||
@ -1046,7 +1070,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onConvertClick() {
|
function onConvertClick() {
|
||||||
submitAction('convert');
|
submitAction('convert');
|
||||||
}
|
}
|
||||||
|
|
||||||
@if ($invoice->id)
|
@if ($invoice->id)
|
||||||
@ -1058,7 +1082,7 @@
|
|||||||
window.location = '{{ URL::to('credits/create/' . $invoice->client->public_id . '/' . $invoice->public_id ) }}';
|
window.location = '{{ URL::to('credits/create/' . $invoice->client->public_id . '/' . $invoice->public_id ) }}';
|
||||||
}
|
}
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
function onArchiveClick() {
|
function onArchiveClick() {
|
||||||
submitBulkAction('archive');
|
submitBulkAction('archive');
|
||||||
}
|
}
|
||||||
@ -1066,7 +1090,7 @@
|
|||||||
function onDeleteClick() {
|
function onDeleteClick() {
|
||||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||||
submitBulkAction('delete');
|
submitBulkAction('delete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formEnterClick(event) {
|
function formEnterClick(event) {
|
||||||
@ -1084,9 +1108,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clientModalEnterClick(event) {
|
function clientModalEnterClick(event) {
|
||||||
if (event.keyCode === 13){
|
if (event.keyCode === 13){
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
model.clientFormComplete();
|
model.clientFormComplete();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1113,7 +1137,7 @@
|
|||||||
var oldVal = val;
|
var oldVal = val;
|
||||||
val = Math.max(Math.min(val, model.invoice().totals.rawTotal()), 0);
|
val = Math.max(Math.min(val, model.invoice().totals.rawTotal()), 0);
|
||||||
model.invoice().partial(val || '');
|
model.invoice().partial(val || '');
|
||||||
|
|
||||||
if (!silent && val != oldVal) {
|
if (!silent && val != oldVal) {
|
||||||
$('#partial').tooltip('show');
|
$('#partial').tooltip('show');
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
@ -29,8 +29,8 @@ function ViewModel(data) {
|
|||||||
if (paymentTerms == -1) paymentTerms = 0;
|
if (paymentTerms == -1) paymentTerms = 0;
|
||||||
var dueDate = $('#invoice_date').datepicker('getDate');
|
var dueDate = $('#invoice_date').datepicker('getDate');
|
||||||
dueDate.setDate(dueDate.getDate() + paymentTerms);
|
dueDate.setDate(dueDate.getDate() + paymentTerms);
|
||||||
self.invoice().due_date(dueDate);
|
self.invoice().due_date(dueDate);
|
||||||
// We're using the datepicker to handle the date formatting
|
// We're using the datepicker to handle the date formatting
|
||||||
self.invoice().due_date($('#due_date').val());
|
self.invoice().due_date($('#due_date').val());
|
||||||
}
|
}
|
||||||
@endif
|
@endif
|
||||||
@ -51,7 +51,7 @@ function ViewModel(data) {
|
|||||||
return new TaxRateModel(options.data);
|
return new TaxRateModel(options.data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
ko.mapping.fromJS(data, self.mapping, self);
|
ko.mapping.fromJS(data, self.mapping, self);
|
||||||
@ -63,7 +63,7 @@ function ViewModel(data) {
|
|||||||
}
|
}
|
||||||
if (self.invoice().tax_rate() > 0) {
|
if (self.invoice().tax_rate() > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ function ViewModel(data) {
|
|||||||
|
|
||||||
self.addTaxRate = function(data) {
|
self.addTaxRate = function(data) {
|
||||||
var itemModel = new TaxRateModel(data);
|
var itemModel = new TaxRateModel(data);
|
||||||
self.tax_rates.push(itemModel);
|
self.tax_rates.push(itemModel);
|
||||||
applyComboboxListeners();
|
applyComboboxListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +200,7 @@ function InvoiceModel(data) {
|
|||||||
self.id = ko.observable('');
|
self.id = ko.observable('');
|
||||||
self.discount = ko.observable('');
|
self.discount = ko.observable('');
|
||||||
self.is_amount_discount = ko.observable(0);
|
self.is_amount_discount = ko.observable(0);
|
||||||
self.frequency_id = ko.observable(4); // default to monthly
|
self.frequency_id = ko.observable(4); // default to monthly
|
||||||
self.terms = ko.observable('');
|
self.terms = ko.observable('');
|
||||||
self.default_terms = ko.observable(account.{{ $entityType }}_terms);
|
self.default_terms = ko.observable(account.{{ $entityType }}_terms);
|
||||||
self.terms_placeholder = ko.observable({{ !$invoice->id && $account->{"{$entityType}_terms"} ? "account.{$entityType}_terms" : false}});
|
self.terms_placeholder = ko.observable({{ !$invoice->id && $account->{"{$entityType}_terms"} ? "account.{$entityType}_terms" : false}});
|
||||||
@ -229,6 +229,7 @@ function InvoiceModel(data) {
|
|||||||
self.invoice_design_id = ko.observable(1);
|
self.invoice_design_id = ko.observable(1);
|
||||||
self.partial = ko.observable(0);
|
self.partial = ko.observable(0);
|
||||||
self.has_tasks = ko.observable();
|
self.has_tasks = ko.observable();
|
||||||
|
self.has_expenses = ko.observable();
|
||||||
|
|
||||||
self.custom_value1 = ko.observable(0);
|
self.custom_value1 = ko.observable(0);
|
||||||
self.custom_value2 = ko.observable(0);
|
self.custom_value2 = ko.observable(0);
|
||||||
@ -260,7 +261,7 @@ function InvoiceModel(data) {
|
|||||||
@if ($account->hide_quantity)
|
@if ($account->hide_quantity)
|
||||||
itemModel.qty(1);
|
itemModel.qty(1);
|
||||||
@endif
|
@endif
|
||||||
self.invoice_items.push(itemModel);
|
self.invoice_items.push(itemModel);
|
||||||
applyComboboxListeners();
|
applyComboboxListeners();
|
||||||
return itemModel;
|
return itemModel;
|
||||||
}
|
}
|
||||||
@ -286,11 +287,11 @@ function InvoiceModel(data) {
|
|||||||
},
|
},
|
||||||
write: function(value) {
|
write: function(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
self._tax(value);
|
self._tax(value);
|
||||||
self.tax_name(value.name());
|
self.tax_name(value.name());
|
||||||
self.tax_rate(value.rate());
|
self.tax_rate(value.rate());
|
||||||
} else {
|
} else {
|
||||||
self._tax(false);
|
self._tax(false);
|
||||||
self.tax_name('');
|
self.tax_name('');
|
||||||
self.tax_rate(0);
|
self.tax_rate(0);
|
||||||
}
|
}
|
||||||
@ -310,7 +311,7 @@ function InvoiceModel(data) {
|
|||||||
|
|
||||||
|
|
||||||
self.wrapped_notes = ko.computed({
|
self.wrapped_notes = ko.computed({
|
||||||
read: function() {
|
read: function() {
|
||||||
return this.public_notes();
|
return this.public_notes();
|
||||||
},
|
},
|
||||||
write: function(value) {
|
write: function(value) {
|
||||||
@ -361,7 +362,7 @@ function InvoiceModel(data) {
|
|||||||
if (parseInt(self.is_amount_discount())) {
|
if (parseInt(self.is_amount_discount())) {
|
||||||
return roundToTwo(self.discount());
|
return roundToTwo(self.discount());
|
||||||
} else {
|
} else {
|
||||||
return roundToTwo(self.totals.rawSubtotal() * (self.discount()/100));
|
return roundToTwo(self.totals.rawSubtotal() * (self.discount()/100));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -416,7 +417,7 @@ function InvoiceModel(data) {
|
|||||||
} else {
|
} else {
|
||||||
taxes[key] = {name:item.tax_name(), rate:item.tax_rate(), amount:taxAmount};
|
taxes[key] = {name:item.tax_name(), rate:item.tax_rate(), amount:taxAmount};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return taxes;
|
return taxes;
|
||||||
});
|
});
|
||||||
@ -432,24 +433,24 @@ function InvoiceModel(data) {
|
|||||||
return count > 0;
|
return count > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
self.totals.itemTaxRates = ko.computed(function() {
|
self.totals.itemTaxRates = ko.computed(function() {
|
||||||
var taxes = self.totals.itemTaxes();
|
var taxes = self.totals.itemTaxes();
|
||||||
var parts = [];
|
var parts = [];
|
||||||
for (var key in taxes) {
|
for (var key in taxes) {
|
||||||
if (taxes.hasOwnProperty(key)) {
|
if (taxes.hasOwnProperty(key)) {
|
||||||
parts.push(taxes[key].name + ' ' + (taxes[key].rate*1) + '%');
|
parts.push(taxes[key].name + ' ' + (taxes[key].rate*1) + '%');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parts.join('<br/>');
|
return parts.join('<br/>');
|
||||||
});
|
});
|
||||||
|
|
||||||
self.totals.itemTaxAmounts = ko.computed(function() {
|
self.totals.itemTaxAmounts = ko.computed(function() {
|
||||||
var taxes = self.totals.itemTaxes();
|
var taxes = self.totals.itemTaxes();
|
||||||
var parts = [];
|
var parts = [];
|
||||||
for (var key in taxes) {
|
for (var key in taxes) {
|
||||||
if (taxes.hasOwnProperty(key)) {
|
if (taxes.hasOwnProperty(key)) {
|
||||||
parts.push(self.formatMoney(taxes[key].amount));
|
parts.push(self.formatMoney(taxes[key].amount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parts.join('<br/>');
|
return parts.join('<br/>');
|
||||||
});
|
});
|
||||||
@ -464,7 +465,7 @@ function InvoiceModel(data) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.totals.rawTotal = ko.computed(function() {
|
self.totals.rawTotal = ko.computed(function() {
|
||||||
var total = accounting.toFixed(self.totals.rawSubtotal(),2);
|
var total = accounting.toFixed(self.totals.rawSubtotal(),2);
|
||||||
var discount = self.totals.rawDiscounted();
|
var discount = self.totals.rawDiscounted();
|
||||||
total -= discount;
|
total -= discount;
|
||||||
|
|
||||||
@ -509,7 +510,7 @@ function InvoiceModel(data) {
|
|||||||
|
|
||||||
self.totals.total = ko.computed(function() {
|
self.totals.total = ko.computed(function() {
|
||||||
return self.formatMoney(self.partial() ? self.partial() : self.totals.rawTotal());
|
return self.formatMoney(self.partial() ? self.partial() : self.totals.rawTotal());
|
||||||
});
|
});
|
||||||
|
|
||||||
self.onDragged = function(item) {
|
self.onDragged = function(item) {
|
||||||
refreshPDF(true);
|
refreshPDF(true);
|
||||||
@ -569,7 +570,7 @@ function ClientModel(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.removeContact = function() {
|
self.removeContact = function() {
|
||||||
self.contacts.remove(this);
|
self.contacts.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.name.display = ko.computed(function() {
|
self.name.display = ko.computed(function() {
|
||||||
@ -577,13 +578,13 @@ function ClientModel(data) {
|
|||||||
return self.name();
|
return self.name();
|
||||||
}
|
}
|
||||||
if (self.contacts().length == 0) return;
|
if (self.contacts().length == 0) return;
|
||||||
var contact = self.contacts()[0];
|
var contact = self.contacts()[0];
|
||||||
if (contact.first_name() || contact.last_name()) {
|
if (contact.first_name() || contact.last_name()) {
|
||||||
return contact.first_name() + ' ' + contact.last_name();
|
return contact.first_name() + ' ' + contact.last_name();
|
||||||
} else {
|
} else {
|
||||||
return contact.email();
|
return contact.email();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.name.placeholder = ko.computed(function() {
|
self.name.placeholder = ko.computed(function() {
|
||||||
if (self.contacts().length == 0) return '';
|
if (self.contacts().length == 0) return '';
|
||||||
@ -593,13 +594,13 @@ function ClientModel(data) {
|
|||||||
} else {
|
} else {
|
||||||
return contact.email();
|
return contact.email();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
ko.mapping.fromJS(data, {}, this);
|
ko.mapping.fromJS(data, {}, this);
|
||||||
} else {
|
} else {
|
||||||
self.addContact();
|
self.addContact();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ContactModel(data) {
|
function ContactModel(data) {
|
||||||
@ -608,7 +609,7 @@ function ContactModel(data) {
|
|||||||
self.first_name = ko.observable('');
|
self.first_name = ko.observable('');
|
||||||
self.last_name = ko.observable('');
|
self.last_name = ko.observable('');
|
||||||
self.email = ko.observable('');
|
self.email = ko.observable('');
|
||||||
self.phone = ko.observable('');
|
self.phone = ko.observable('');
|
||||||
self.send_invoice = ko.observable(false);
|
self.send_invoice = ko.observable(false);
|
||||||
self.invitation_link = ko.observable('');
|
self.invitation_link = ko.observable('');
|
||||||
self.invitation_status = ko.observable('');
|
self.invitation_status = ko.observable('');
|
||||||
@ -623,10 +624,10 @@ function ContactModel(data) {
|
|||||||
var str = '';
|
var str = '';
|
||||||
if (self.first_name() || self.last_name()) {
|
if (self.first_name() || self.last_name()) {
|
||||||
str += self.first_name() + ' ' + self.last_name() + '\n';
|
str += self.first_name() + ' ' + self.last_name() + '\n';
|
||||||
}
|
}
|
||||||
if (self.email()) {
|
if (self.email()) {
|
||||||
str += self.email() + '\n';
|
str += self.email() + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
});
|
});
|
||||||
@ -635,9 +636,9 @@ function ContactModel(data) {
|
|||||||
var str = '';
|
var str = '';
|
||||||
if (self.first_name() || self.last_name()) {
|
if (self.first_name() || self.last_name()) {
|
||||||
str += self.first_name() + ' ' + self.last_name() + '<br/>';
|
str += self.first_name() + ' ' + self.last_name() + '<br/>';
|
||||||
}
|
}
|
||||||
if (self.email()) {
|
if (self.email()) {
|
||||||
str += self.email() + '<br/>';
|
str += self.email() + '<br/>';
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
});
|
});
|
||||||
@ -651,7 +652,7 @@ function ContactModel(data) {
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function TaxRateModel(data) {
|
function TaxRateModel(data) {
|
||||||
@ -675,7 +676,7 @@ function TaxRateModel(data) {
|
|||||||
this.rate(value);
|
this.rate(value);
|
||||||
},
|
},
|
||||||
owner: this
|
owner: this
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
self.displayName = ko.computed({
|
self.displayName = ko.computed({
|
||||||
@ -687,8 +688,8 @@ function TaxRateModel(data) {
|
|||||||
write: function (value) {
|
write: function (value) {
|
||||||
// do nothing
|
// do nothing
|
||||||
},
|
},
|
||||||
owner: this
|
owner: this
|
||||||
});
|
});
|
||||||
|
|
||||||
self.hideActions = function() {
|
self.hideActions = function() {
|
||||||
self.actionsVisible(false);
|
self.actionsVisible(false);
|
||||||
@ -696,15 +697,15 @@ function TaxRateModel(data) {
|
|||||||
|
|
||||||
self.showActions = function() {
|
self.showActions = function() {
|
||||||
self.actionsVisible(true);
|
self.actionsVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isEmpty = function() {
|
self.isEmpty = function() {
|
||||||
return !self.rate() && !self.name();
|
return !self.rate() && !self.name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ItemModel(data) {
|
function ItemModel(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.product_key = ko.observable('');
|
self.product_key = ko.observable('');
|
||||||
self.notes = ko.observable('');
|
self.notes = ko.observable('');
|
||||||
self.cost = ko.observable(0);
|
self.cost = ko.observable(0);
|
||||||
@ -712,6 +713,7 @@ function ItemModel(data) {
|
|||||||
self.tax_name = ko.observable('');
|
self.tax_name = ko.observable('');
|
||||||
self.tax_rate = ko.observable(0);
|
self.tax_rate = ko.observable(0);
|
||||||
self.task_public_id = ko.observable('');
|
self.task_public_id = ko.observable('');
|
||||||
|
self.expense_public_id = ko.observable('');
|
||||||
self.actionsVisible = ko.observable(false);
|
self.actionsVisible = ko.observable(false);
|
||||||
|
|
||||||
self._tax = ko.observable();
|
self._tax = ko.observable();
|
||||||
@ -720,7 +722,7 @@ function ItemModel(data) {
|
|||||||
return self._tax();
|
return self._tax();
|
||||||
},
|
},
|
||||||
write: function(value) {
|
write: function(value) {
|
||||||
self._tax(value);
|
self._tax(value);
|
||||||
self.tax_name(value.name());
|
self.tax_name(value.name());
|
||||||
self.tax_rate(value.rate());
|
self.tax_rate(value.rate());
|
||||||
}
|
}
|
||||||
@ -734,7 +736,7 @@ function ItemModel(data) {
|
|||||||
this.qty(value);
|
this.qty(value);
|
||||||
},
|
},
|
||||||
owner: this
|
owner: this
|
||||||
});
|
});
|
||||||
|
|
||||||
this.prettyCost = ko.computed({
|
this.prettyCost = ko.computed({
|
||||||
read: function () {
|
read: function () {
|
||||||
@ -744,7 +746,7 @@ function ItemModel(data) {
|
|||||||
this.cost(value);
|
this.cost(value);
|
||||||
},
|
},
|
||||||
owner: this
|
owner: this
|
||||||
});
|
});
|
||||||
|
|
||||||
self.mapping = {
|
self.mapping = {
|
||||||
'tax': {
|
'tax': {
|
||||||
@ -755,7 +757,7 @@ function ItemModel(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
ko.mapping.fromJS(data, self.mapping, this);
|
ko.mapping.fromJS(data, self.mapping, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.wrapped_notes = ko.computed({
|
self.wrapped_notes = ko.computed({
|
||||||
@ -775,7 +777,7 @@ function ItemModel(data) {
|
|||||||
this.totals.rawTotal = ko.computed(function() {
|
this.totals.rawTotal = ko.computed(function() {
|
||||||
var cost = roundToTwo(NINJA.parseFloat(self.cost()));
|
var cost = roundToTwo(NINJA.parseFloat(self.cost()));
|
||||||
var qty = roundToTwo(NINJA.parseFloat(self.qty()));
|
var qty = roundToTwo(NINJA.parseFloat(self.qty()));
|
||||||
var value = cost * qty;
|
var value = cost * qty;
|
||||||
return value ? roundToTwo(value) : 0;
|
return value ? roundToTwo(value) : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -806,4 +808,4 @@ function ItemModel(data) {
|
|||||||
this.onSelect = function() {}
|
this.onSelect = function() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user