mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 12:12:48 +01:00
Creating re-usable products from invoice line items
This commit is contained in:
parent
7b41fbc779
commit
426e7838c9
@ -42,7 +42,7 @@ class InvoiceController extends \BaseController {
|
|||||||
$invoice = Invoice::with('invoice_items', 'client.account.account_gateways')->where('invoice_key', '=', $invoiceKey)->firstOrFail();
|
$invoice = Invoice::with('invoice_items', 'client.account.account_gateways')->where('invoice_key', '=', $invoiceKey)->firstOrFail();
|
||||||
$contact = null;
|
$contact = null;
|
||||||
|
|
||||||
Activity::viewInvoice($invoice, $contact)
|
Activity::viewInvoice($invoice, $contact);
|
||||||
|
|
||||||
return View::make('invoices.view')->with('invoice', $invoice);
|
return View::make('invoices.view')->with('invoice', $invoice);
|
||||||
}
|
}
|
||||||
@ -181,6 +181,7 @@ class InvoiceController extends \BaseController {
|
|||||||
'title' => 'New',
|
'title' => 'New',
|
||||||
'client' => $client,
|
'client' => $client,
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
|
'products' => Product::getProducts()->get(),
|
||||||
'clients' => Client::where('account_id','=',Auth::user()->account_id)->get());
|
'clients' => Client::where('account_id','=',Auth::user()->account_id)->get());
|
||||||
return View::make('invoices.edit', $data);
|
return View::make('invoices.edit', $data);
|
||||||
}
|
}
|
||||||
@ -241,7 +242,7 @@ class InvoiceController extends \BaseController {
|
|||||||
|
|
||||||
$invoice->client_id = $clientId;
|
$invoice->client_id = $clientId;
|
||||||
$invoice->number = Input::get('number');
|
$invoice->number = Input::get('number');
|
||||||
$invoice->discount = Input::get('discount');
|
$invoice->discount = 0;
|
||||||
$invoice->issued_on = $date->format('Y-m-d');
|
$invoice->issued_on = $date->format('Y-m-d');
|
||||||
$invoice->save();
|
$invoice->save();
|
||||||
|
|
||||||
@ -252,11 +253,17 @@ class InvoiceController extends \BaseController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$product = new Product;
|
$product = Product::findProduct($item->product_key);
|
||||||
$product->product_key = $item->product_key;
|
|
||||||
$product->notes = $item->notes;
|
if (!$product)
|
||||||
$product->cost = $item->cost;
|
{
|
||||||
$product->save();
|
$product = new Product;
|
||||||
|
$product->account_id = Auth::user()->account_id;
|
||||||
|
$product->product_key = $item->product_key;
|
||||||
|
$product->notes = $item->notes;
|
||||||
|
$product->cost = $item->cost;
|
||||||
|
$product->save();
|
||||||
|
}
|
||||||
|
|
||||||
$invoiceItem = new InvoiceItem;
|
$invoiceItem = new InvoiceItem;
|
||||||
$invoiceItem->product_id = $product->id;
|
$invoiceItem->product_id = $product->id;
|
||||||
@ -316,6 +323,7 @@ class InvoiceController extends \BaseController {
|
|||||||
'url' => 'invoices/' . $id,
|
'url' => 'invoices/' . $id,
|
||||||
'title' => 'Edit',
|
'title' => 'Edit',
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
|
'products' => Product::getProducts()->get(),
|
||||||
'client' => $invoice->client,
|
'client' => $invoice->client,
|
||||||
'clients' => Client::where('account_id','=',Auth::user()->account_id)->get());
|
'clients' => Client::where('account_id','=',Auth::user()->account_id)->get());
|
||||||
return View::make('invoices.edit', $data);
|
return View::make('invoices.edit', $data);
|
||||||
|
@ -18,7 +18,7 @@ class Activity extends Eloquent
|
|||||||
$activity->user_id = $user->id;
|
$activity->user_id = $user->id;
|
||||||
$activity->account_id = $user->account_id;
|
$activity->account_id = $user->account_id;
|
||||||
|
|
||||||
return $user;
|
return $activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function createClient($client)
|
public static function createClient($client)
|
||||||
@ -26,7 +26,7 @@ class Activity extends Eloquent
|
|||||||
$activity = Activity::getBlank();
|
$activity = Activity::getBlank();
|
||||||
$activity->client_id = $client->id;
|
$activity->client_id = $client->id;
|
||||||
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_CLIENT;
|
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_CLIENT;
|
||||||
$activity->message = $user->getFullName() . ' created client ' . $client->name;
|
$activity->message = Auth::user()->getFullName() . ' created client ' . $client->name;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ class Activity extends Eloquent
|
|||||||
$activity = Activity::getBlank();
|
$activity = Activity::getBlank();
|
||||||
$activity->client_id = $client->id;
|
$activity->client_id = $client->id;
|
||||||
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_CLIENT;
|
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_CLIENT;
|
||||||
$activity->message = $user->getFullName() . ' archived client ' . $client->name;
|
$activity->message = Auth::user()->getFullName() . ' archived client ' . $client->name;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class Activity extends Eloquent
|
|||||||
$activity->invoice_id = $invoice->id;
|
$activity->invoice_id = $invoice->id;
|
||||||
$activity->client_id = $invoice->client_id;
|
$activity->client_id = $invoice->client_id;
|
||||||
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_INVOICE;
|
$activity->activity_type_id = ACTIVITY_TYPE_CREATE_INVOICE;
|
||||||
$activity->message = $user->getFullName() . ' created invoice ' . $invoice->number;
|
$activity->message = Auth::user()->getFullName() . ' created invoice ' . $invoice->number;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ class Activity extends Eloquent
|
|||||||
$activity->invoice_id = $invoice->id;
|
$activity->invoice_id = $invoice->id;
|
||||||
$activity->client_id = $invoice->client_id;
|
$activity->client_id = $invoice->client_id;
|
||||||
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_INVOICE;
|
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_INVOICE;
|
||||||
$activity->message = $user->getFullName() . ' archived invoice ' . $invoice->number;
|
$activity->message = Auth::user()->getFullName() . ' archived invoice ' . $invoice->number;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class Activity extends Eloquent
|
|||||||
$activity->invoice_id = $invoice->id;
|
$activity->invoice_id = $invoice->id;
|
||||||
$activity->client_id = $invoice->client_id;
|
$activity->client_id = $invoice->client_id;
|
||||||
$activity->activity_type_id = ACTIVITY_TYPE_EMAIL_INVOICE;
|
$activity->activity_type_id = ACTIVITY_TYPE_EMAIL_INVOICE;
|
||||||
$activity->message = $user->getFullName() . ' emailed invoice ' . $invoice->number . ' to ' . $contact->getFullName();
|
$activity->message = Auth::user()->getFullName() . ' emailed invoice ' . $invoice->number . ' to ' . $contact->getFullName();
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ class Activity extends Eloquent
|
|||||||
if (Auth::check())
|
if (Auth::check())
|
||||||
{
|
{
|
||||||
$activity = Activity::getBlank();
|
$activity = Activity::getBlank();
|
||||||
$activity->message = $user->getFullName() . ' created invoice ' . $payment->transaction_reference;
|
$activity->message = Auth::user()->getFullName() . ' created invoice ' . $payment->transaction_reference;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -96,7 +96,7 @@ class Activity extends Eloquent
|
|||||||
$activity->invoice_id = $invoice->id;
|
$activity->invoice_id = $invoice->id;
|
||||||
$activity->client_id = $invoice->client_id;
|
$activity->client_id = $invoice->client_id;
|
||||||
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_PAYMENT;
|
$activity->activity_type_id = ACTIVITY_TYPE_ARCHIVE_PAYMENT;
|
||||||
$activity->message = $user->getFullName() . ' archived payment ' . $invoice->number;
|
$activity->message = Auth::user()->getFullName() . ' archived payment ' . $invoice->number;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,4 +3,22 @@
|
|||||||
class Product extends Eloquent
|
class Product extends Eloquent
|
||||||
{
|
{
|
||||||
protected $softDelete = true;
|
protected $softDelete = true;
|
||||||
|
|
||||||
|
public static function getProducts()
|
||||||
|
{
|
||||||
|
return Product::where('account_id','=',Auth::user()->account_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function findProduct($key)
|
||||||
|
{
|
||||||
|
return Product::getProducts()->where('product_key','=',$key)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getProductKeys($products)
|
||||||
|
{
|
||||||
|
$products = array_pluck($products, 'product_key');
|
||||||
|
$products = array_combine($products, $products);
|
||||||
|
|
||||||
|
return $products;
|
||||||
|
}
|
||||||
}
|
}
|
@ -97,6 +97,10 @@ function toArray($data)
|
|||||||
return json_decode(json_encode((array) $data), true);
|
return json_decode(json_encode((array) $data), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define("ENV_DEVELOPMENT", "local");
|
||||||
|
define("ENV_STAGING", "staging");
|
||||||
|
define("ENV_PRODUCTION", "production");
|
||||||
|
|
||||||
define("ACCOUNT_DETAILS", "details");
|
define("ACCOUNT_DETAILS", "details");
|
||||||
define("ACCOUNT_SETTINGS", "settings");
|
define("ACCOUNT_SETTINGS", "settings");
|
||||||
define("ACCOUNT_IMPORT", "import");
|
define("ACCOUNT_IMPORT", "import");
|
||||||
|
@ -197,7 +197,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.invoice-table td {
|
.invoice-table td {
|
||||||
padding: 0px !important;
|
padding: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-table td input,
|
.invoice-table td input,
|
||||||
@ -225,6 +225,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@if (App::environment() != ENV_DEVELOPMENT)
|
||||||
<script>
|
<script>
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
@ -235,6 +236,8 @@
|
|||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<p/>
|
<p/>
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<p> </p>
|
<p> </p>
|
||||||
|
|
||||||
<input type="text" name="items" data-bind="value: ko.toJSON(items)" style="display:none"/>
|
<input type="text" name="items" data-bind="value: ko.toJSON(items)" style="display:none"/>
|
||||||
<table class="table invoice-table" style="margin-bottom: 0px !important;">
|
<table class="table invoice-table" style="margin-bottom: 0px !important">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="hide-border"></th>
|
<th class="hide-border"></th>
|
||||||
@ -51,23 +51,23 @@
|
|||||||
<i data-bind="visible: actionsVisible" class="fa fa-sort"></i>
|
<i data-bind="visible: actionsVisible" class="fa fa-sort"></i>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:120px">
|
<td style="width:120px">
|
||||||
<input data-bind="value: product_key, valueUpdate: 'afterkeydown'" onchange="refreshPDF()"/>
|
{{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'product_key')->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }}
|
||||||
</td>
|
</td>
|
||||||
<td style="width:300px">
|
<td style="width:300px">
|
||||||
<textarea data-bind="value: notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" onchange="refreshPDF()"></textarea>
|
<textarea data-bind="value: notes, valueUpdate: 'afterkeydown'" rows="1" cols="60" class="form-control" onchange="refreshPDF()"></textarea>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:100px">
|
<td style="width:100px">
|
||||||
<input data-bind="value: cost, valueUpdate: 'afterkeydown'" style="text-align: right" onchange="refreshPDF()"//>
|
<input data-bind="value: cost, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:80px">
|
<td style="width:80px">
|
||||||
<input data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" onchange="refreshPDF()"//>
|
<input data-bind="value: qty, valueUpdate: 'afterkeydown'" style="text-align: right" class="form-control" onchange="refreshPDF()"//>
|
||||||
</td>
|
</td>
|
||||||
<!--
|
<!--
|
||||||
<td style="width:100px">
|
<td style="width:100px">
|
||||||
<input data-bind="value: tax, valueUpdate: 'afterkeydown'"/>
|
<input data-bind="value: tax, valueUpdate: 'afterkeydown'"/>
|
||||||
</td>
|
</td>
|
||||||
-->
|
-->
|
||||||
<td style="width:100px;background-color: #FFFFFF;text-align: right">
|
<td style="width:100px;background-color: #FFFFFF;text-align: right;padding-top:9px !important">
|
||||||
<span data-bind="text: total"></span>
|
<span data-bind="text: total"></span>
|
||||||
</td>
|
</td>
|
||||||
<td style="width:20px; cursor:pointer" class="hide-border">
|
<td style="width:20px; cursor:pointer" class="hide-border">
|
||||||
@ -106,9 +106,8 @@
|
|||||||
<p> </p>
|
<p> </p>
|
||||||
|
|
||||||
<!-- <textarea rows="20" cols="120" id="pdfText" onkeyup="runCode()"></textarea> -->
|
<!-- <textarea rows="20" cols="120" id="pdfText" onkeyup="runCode()"></textarea> -->
|
||||||
<!-- <iframe frameborder="1" width="600" height="600" style="display:block;margin: 0 auto"></iframe> -->
|
<!-- <iframe frameborder="1" width="100%" height="600" style="display:block;margin: 0 auto"></iframe> -->
|
||||||
<iframe frameborder="1" width="92%" height="600" style="display:block;margin: 0 auto"></iframe>
|
<iframe frameborder="1" width="100%" height="500"></iframe>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||||
@ -159,6 +158,7 @@
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
$('#issued_on').datepicker({
|
$('#issued_on').datepicker({
|
||||||
@ -192,10 +192,31 @@
|
|||||||
|
|
||||||
$('#number').change(refreshPDF);
|
$('#number').change(refreshPDF);
|
||||||
|
|
||||||
refreshPDF();
|
applyComboboxListeners();
|
||||||
|
refreshPDF();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function applyComboboxListeners() {
|
||||||
|
var value;
|
||||||
|
$('.datalist').on('focus', function() {
|
||||||
|
value = $(this).val();
|
||||||
|
}).on('blur', function() {
|
||||||
|
if (value != $(this).val()) refreshPDF();
|
||||||
|
}).on('input', function() {
|
||||||
|
var key = $(this).val();
|
||||||
|
for (var i=0; i<products.length; i++) {
|
||||||
|
var product = products[i];
|
||||||
|
if (product.product_key == key) {
|
||||||
|
var model = ko.dataFor(this);
|
||||||
|
console.log(model);
|
||||||
|
model.notes(product.notes);
|
||||||
|
model.cost(product.cost);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function runCode() {
|
function runCode() {
|
||||||
var text = $('#pdfText').val();
|
var text = $('#pdfText').val();
|
||||||
eval(text);
|
eval(text);
|
||||||
@ -317,6 +338,7 @@
|
|||||||
|
|
||||||
self.addItem = function() {
|
self.addItem = function() {
|
||||||
self.items.push(new ItemModel());
|
self.items.push(new ItemModel());
|
||||||
|
applyComboboxListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rawSubtotal = ko.computed(function() {
|
this.rawSubtotal = ko.computed(function() {
|
||||||
@ -389,9 +411,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var products = {{ $products }};
|
||||||
|
|
||||||
window.model = new InvoiceModel();
|
window.model = new InvoiceModel();
|
||||||
ko.applyBindings(model);
|
ko.applyBindings(model);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@if (App::environment() != ENV_DEVELOPMENT)
|
||||||
<script>
|
<script>
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
@ -29,8 +31,9 @@
|
|||||||
|
|
||||||
ga('create', 'UA-46031341-1', 'sketch-out.com');
|
ga('create', 'UA-46031341-1', 'sketch-out.com');
|
||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
|
@ -302,3 +302,5 @@ function isStorageSupported() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user