invoiceninja/app/Http/Controllers/InvoiceApiController.php

482 lines
15 KiB
PHP
Raw Normal View History

2017-01-30 21:40:43 +02:00
<?php
2015-03-17 07:45:25 +10:00
2017-01-30 21:40:43 +02:00
namespace App\Http\Controllers;
use App\Http\Requests\InvoiceRequest;
use App\Http\Requests\CreateInvoiceAPIRequest;
2017-01-30 21:40:43 +02:00
use App\Http\Requests\UpdateInvoiceAPIRequest;
use App\Jobs\SendInvoiceEmail;
use App\Jobs\SendPaymentEmail;
2015-04-08 21:19:58 +03:00
use App\Models\Client;
2015-05-10 11:45:03 +03:00
use App\Models\Contact;
2017-01-30 21:40:43 +02:00
use App\Models\Invoice;
2015-04-08 21:19:58 +03:00
use App\Models\Product;
2015-05-10 11:45:03 +03:00
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\InvoiceRepository;
2017-01-30 21:40:43 +02:00
use App\Ninja\Repositories\PaymentRepository;
2016-02-18 15:46:44 +02:00
use App\Services\InvoiceService;
2016-06-23 12:57:10 +03:00
use App\Services\PaymentService;
2017-01-30 21:40:43 +02:00
use Auth;
use Input;
use Response;
use Utils;
use Validator;
2015-03-17 07:45:25 +10:00
2015-11-27 14:55:28 +02:00
class InvoiceApiController extends BaseAPIController
2015-03-17 07:45:25 +10:00
{
protected $invoiceRepo;
2016-05-01 23:55:13 +03:00
protected $entityType = ENTITY_INVOICE;
2017-01-11 19:37:42 +02:00
public function __construct(InvoiceService $invoiceService, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, PaymentRepository $paymentRepo, PaymentService $paymentService)
2015-03-17 07:45:25 +10:00
{
2016-03-02 16:36:46 +02:00
parent::__construct();
2015-11-27 14:55:28 +02:00
2015-03-17 07:45:25 +10:00
$this->invoiceRepo = $invoiceRepo;
2015-05-10 11:45:03 +03:00
$this->clientRepo = $clientRepo;
2016-02-16 17:30:09 +02:00
$this->paymentRepo = $paymentRepo;
2016-02-18 15:46:44 +02:00
$this->invoiceService = $invoiceService;
2016-06-23 12:57:10 +03:00
$this->paymentService = $paymentService;
2015-03-17 07:45:25 +10:00
}
2015-11-08 22:34:26 +02:00
/**
* @SWG\Get(
* path="/invoices",
* summary="List invoices",
* operationId="listInvoices",
2015-11-08 23:53:13 +02:00
* tags={"invoice"},
2015-11-08 22:34:26 +02:00
* @SWG\Response(
* response=200,
* description="A list of invoices",
2015-11-08 22:34:26 +02:00
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Invoice"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
2015-11-27 14:55:28 +02:00
public function index()
2015-03-17 07:45:25 +10:00
{
2016-05-01 23:55:13 +03:00
$invoices = Invoice::scope()
->withTrashed()
2016-05-04 14:53:27 +03:00
->with('invoice_items', 'client')
2016-05-01 23:55:13 +03:00
->orderBy('created_at', 'desc');
2015-11-27 14:55:28 +02:00
2017-03-19 18:03:17 +02:00
// Filter by invoice number
if ($invoiceNumber = Input::get('invoice_number')) {
$invoices->whereInvoiceNumber($invoiceNumber);
}
// Fllter by status
if ($statusId = Input::get('status_id')) {
$invoices->where('invoice_status_id', '>=', $statusId);
}
2018-05-06 22:45:47 +03:00
if (request()->has('is_recurring')) {
$invoices->where('is_recurring', '=', request()->is_recurring);
}
2018-05-18 09:41:55 +03:00
if (request()->has('invoice_type_id')) {
$invoices->where('invoice_type_id', '=', request()->invoice_type_id);
}
2016-05-02 11:38:01 +03:00
return $this->listResponse($invoices);
2015-03-17 07:45:25 +10:00
}
2017-01-30 21:40:43 +02:00
/**
* @SWG\Get(
* path="/invoices/{invoice_id}",
* summary="Retrieve an Invoice",
2017-01-30 21:40:43 +02:00
* tags={"invoice"},
* @SWG\Parameter(
* in="path",
* name="invoice_id",
* type="integer",
* required=true
* ),
2017-01-30 21:40:43 +02:00
* @SWG\Response(
* response=200,
* description="A single invoice",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Invoice"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
2016-05-02 16:45:12 +03:00
public function show(InvoiceRequest $request)
2016-02-17 21:16:13 +11:00
{
2016-05-02 16:45:12 +03:00
return $this->itemResponse($request->entity());
2016-02-17 21:16:13 +11:00
}
2015-11-08 23:53:13 +02:00
/**
* @SWG\Post(
* path="/invoices",
* summary="Create an invoice",
* tags={"invoice"},
2015-11-08 23:53:13 +02:00
* @SWG\Parameter(
* in="body",
* name="invoice",
2015-11-08 23:53:13 +02:00
* @SWG\Schema(ref="#/definitions/Invoice")
* ),
* @SWG\Response(
* response=200,
2015-11-08 23:57:28 +02:00
* description="New invoice",
2015-11-08 23:53:13 +02:00
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Invoice"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
2016-05-01 22:30:39 +03:00
public function store(CreateInvoiceAPIRequest $request)
2015-03-17 07:45:25 +10:00
{
$data = Input::all();
$error = null;
2015-10-28 21:22:07 +02:00
2015-05-10 11:45:03 +03:00
if (isset($data['email'])) {
$email = $data['email'];
2017-01-30 18:05:31 +02:00
$client = Client::scope()->whereHas('contacts', function ($query) use ($email) {
$query->where('email', '=', $email);
2015-05-11 14:16:36 +03:00
})->first();
2016-03-02 15:36:42 +02:00
2017-01-30 21:40:43 +02:00
if (! $client) {
$validator = Validator::make(['email' => $email], ['email' => 'email']);
if ($validator->fails()) {
2015-11-18 19:16:23 +02:00
$messages = $validator->messages();
2017-01-30 21:40:43 +02:00
2015-11-18 19:16:23 +02:00
return $messages->first();
}
$clientData = ['contact' => ['email' => $email]];
2016-02-16 17:30:09 +02:00
foreach ([
'name',
'address1',
'address2',
'city',
'state',
'postal_code',
2016-05-29 12:26:02 +03:00
'country_id',
2016-02-16 17:30:09 +02:00
'private_notes',
'currency_code',
'country_code',
2016-02-16 17:30:09 +02:00
] as $field) {
2015-05-10 11:45:03 +03:00
if (isset($data[$field])) {
$clientData[$field] = $data[$field];
}
}
2016-02-16 17:30:09 +02:00
foreach ([
'first_name',
'last_name',
'phone',
] as $field) {
2015-05-10 11:45:03 +03:00
if (isset($data[$field])) {
2016-02-16 17:30:09 +02:00
$clientData['contact'][$field] = $data[$field];
2015-05-10 11:45:03 +03:00
}
}
$client = $this->clientRepo->save($clientData);
2015-05-10 11:45:03 +03:00
}
2017-01-30 18:05:31 +02:00
} elseif (isset($data['client_id'])) {
2018-06-17 12:56:44 +03:00
$client = Client::scope($data['client_id'])->first();
if (! $client) {
return $this->errorResponse('Client not found', 404);
}
2015-05-10 11:45:03 +03:00
}
$data = self::prepareData($data, $client);
$data['client_id'] = $client->id;
// in these cases the invoice needs to be set as public
$isAutoBill = isset($data['auto_bill']) && filter_var($data['auto_bill'], FILTER_VALIDATE_BOOLEAN);
$isEmailInvoice = isset($data['email_invoice']) && filter_var($data['email_invoice'], FILTER_VALIDATE_BOOLEAN);
$isPaid = isset($data['paid']) && floatval($data['paid']);
if ($isAutoBill || $isPaid || $isEmailInvoice) {
$data['is_public'] = true;
}
2016-02-18 15:46:44 +02:00
$invoice = $this->invoiceService->save($data);
2016-02-16 17:30:09 +02:00
$payment = false;
2017-05-22 11:22:34 +03:00
if ($invoice->isStandard()) {
if ($isAutoBill) {
$payment = $this->paymentService->autoBillInvoice($invoice);
2017-01-30 18:05:31 +02:00
} elseif ($isPaid) {
$payment = $this->paymentRepo->save([
'invoice_id' => $invoice->id,
'client_id' => $client->id,
2018-04-08 10:49:54 +03:00
'amount' => round($data['paid'], 2),
]);
}
2016-02-16 17:30:09 +02:00
}
2015-10-22 21:48:12 +03:00
if ($isEmailInvoice) {
2016-02-16 17:30:09 +02:00
if ($payment) {
$this->dispatch(new SendPaymentEmail($payment));
} else {
if ($invoice->is_recurring && $recurringInvoice = $this->invoiceRepo->createRecurringInvoice($invoice)) {
$invoice = $recurringInvoice;
}
$reminder = isset($data['email_type']) ? $data['email_type'] : false;
$this->dispatch(new SendInvoiceEmail($invoice, auth()->user()->id, $reminder));
2016-02-16 17:30:09 +02:00
}
}
2015-05-10 11:45:03 +03:00
2016-05-02 16:45:12 +03:00
$invoice = Invoice::scope($invoice->public_id)
->with('client', 'invoice_items', 'invitations')
->first();
2016-05-29 12:26:02 +03:00
2016-06-23 12:38:06 +03:00
if (isset($data['download_invoice']) && boolval($data['download_invoice'])) {
return $this->fileReponse($invoice->getFileName(), $invoice->getPDFString());
}
2016-05-02 16:45:12 +03:00
return $this->itemResponse($invoice);
2015-03-17 07:45:25 +10:00
}
private function prepareData($data, $client)
2015-03-17 07:45:25 +10:00
{
$account = Auth::user()->account;
$account->loadLocalizationSettings($client);
2016-03-02 15:36:42 +02:00
2015-03-17 07:45:25 +10:00
// set defaults for optional fields
$fields = [
'discount' => 0,
'is_amount_discount' => false,
'terms' => '',
'invoice_footer' => '',
'public_notes' => '',
'po_number' => '',
'invoice_design_id' => $account->invoice_design_id,
'invoice_items' => [],
'custom_taxes1' => false,
'custom_taxes2' => false,
'tax_name1' => '',
'tax_rate1' => 0,
'tax_name2' => '',
'tax_rate2' => 0,
2017-01-30 21:40:43 +02:00
'partial' => 0,
2015-03-17 07:45:25 +10:00
];
2017-01-30 21:40:43 +02:00
if (! isset($data['invoice_status_id']) || $data['invoice_status_id'] == 0) {
2016-01-27 21:00:00 +11:00
$data['invoice_status_id'] = INVOICE_STATUS_DRAFT;
}
2017-01-30 21:40:43 +02:00
if (! isset($data['invoice_date'])) {
2017-01-30 14:40:07 +02:00
$fields['invoice_date_sql'] = date_create()->format('Y-m-d');
2015-03-17 07:45:25 +10:00
}
2017-01-30 21:40:43 +02:00
if (! isset($data['due_date'])) {
2015-03-17 07:45:25 +10:00
$fields['due_date_sql'] = false;
}
2017-06-01 19:13:13 +03:00
if (isset($data['is_quote']) && filter_var($data['is_quote'], FILTER_VALIDATE_BOOLEAN)) {
$fields['invoice_design_id'] = $account->quote_design_id;
}
2015-03-17 07:45:25 +10:00
foreach ($fields as $key => $val) {
2017-01-30 21:40:43 +02:00
if (! isset($data[$key])) {
2015-03-17 07:45:25 +10:00
$data[$key] = $val;
}
}
// initialize the line items
2018-05-06 22:45:47 +03:00
if (! isset($data['invoice_items']) && (isset($data['product_key']) || isset($data['cost']) || isset($data['notes']) || isset($data['qty']))) {
2015-03-17 07:45:25 +10:00
$data['invoice_items'] = [self::prepareItem($data)];
2015-10-13 10:11:44 +03:00
// make sure the tax isn't applied twice (for the invoice and the line item)
2016-03-31 12:29:01 +03:00
unset($data['invoice_items'][0]['tax_name1']);
unset($data['invoice_items'][0]['tax_rate1']);
unset($data['invoice_items'][0]['tax_name2']);
unset($data['invoice_items'][0]['tax_rate2']);
2015-03-17 07:45:25 +10:00
} else {
foreach ($data['invoice_items'] as $index => $item) {
// check for multiple products
if ($productKey = array_get($item, 'product_key')) {
$parts = explode(',', $productKey);
if (count($parts) > 1 && Product::findProductByKey($parts[0])) {
foreach ($parts as $index => $productKey) {
$data['invoice_items'][$index] = self::prepareItem(['product_key' => $productKey]);
}
break;
}
}
2015-03-17 07:45:25 +10:00
$data['invoice_items'][$index] = self::prepareItem($item);
}
}
return $data;
}
private function prepareItem($item)
{
// if only the product key is set we'll load the cost and notes
if (! empty($item['product_key'])) {
$product = Product::findProductByKey($item['product_key']);
if ($product) {
$fields = [
'cost',
'notes',
'custom_value1',
'custom_value2',
2017-10-18 12:10:59 +03:00
'tax_name1',
'tax_rate1',
'tax_name2',
'tax_rate2',
];
foreach ($fields as $field) {
if (! isset($item[$field])) {
$item[$field] = $product->$field;
}
}
}
}
2015-03-17 07:45:25 +10:00
$fields = [
'cost' => 0,
'product_key' => '',
'notes' => '',
2017-01-30 21:40:43 +02:00
'qty' => 1,
2015-03-17 07:45:25 +10:00
];
foreach ($fields as $key => $val) {
2017-01-30 21:40:43 +02:00
if (! isset($item[$key])) {
2015-03-17 07:45:25 +10:00
$item[$key] = $val;
}
}
2016-05-29 12:26:02 +03:00
2018-05-06 22:45:47 +03:00
// Workaround to support line item taxes w/Zapier
foreach (['tax_rate1', 'tax_name1', 'tax_rate2', 'tax_name2'] as $field) {
if (! empty($item['item_' . $field])) {
$item[$field] = $item['item_' . $field];
}
}
2015-03-17 07:45:25 +10:00
return $item;
}
2016-05-02 16:45:12 +03:00
public function emailInvoice(InvoiceRequest $request)
2016-01-29 12:47:35 +11:00
{
2016-05-02 16:45:12 +03:00
$invoice = $request->entity();
2016-01-29 12:47:35 +11:00
if ($invoice->is_recurring && $recurringInvoice = $this->invoiceRepo->createRecurringInvoice($invoice)) {
$invoice = $recurringInvoice;
}
if (config('queue.default') !== 'sync') {
$this->dispatch(new SendInvoiceEmail($invoice, auth()->user()->id));
} else {
$result = app('App\Ninja\Mailers\ContactMailer')->sendInvoice($invoice);
if ($result !== true) {
return $this->errorResponse($result, 500);
}
}
2016-01-29 12:47:35 +11:00
$headers = Utils::getApiHeaders();
2017-05-16 20:34:04 +10:00
$response = json_encode(['message' => RESULT_SUCCESS], JSON_PRETTY_PRINT);
2017-01-30 21:40:43 +02:00
2016-05-02 16:45:12 +03:00
return Response::make($response, 200, $headers);
2016-01-29 12:47:35 +11:00
}
2017-01-30 21:40:43 +02:00
/**
* @SWG\Put(
* path="/invoices/{invoice_id}",
2017-01-30 21:40:43 +02:00
* summary="Update an invoice",
* tags={"invoice"},
2017-01-30 21:40:43 +02:00
* @SWG\Parameter(
* in="path",
* name="invoice_id",
* type="integer",
* required=true
* ),
* @SWG\Parameter(
2017-01-30 21:40:43 +02:00
* in="body",
* name="invoice",
2017-01-30 21:40:43 +02:00
* @SWG\Schema(ref="#/definitions/Invoice")
* ),
* @SWG\Response(
* response=200,
* description="Updated invoice",
2017-01-30 21:40:43 +02:00
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Invoice"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
2017-01-30 21:54:09 +02:00
*
2017-01-30 21:49:42 +02:00
* @param mixed $publicId
2017-01-30 21:40:43 +02:00
*/
2016-05-01 22:30:39 +03:00
public function update(UpdateInvoiceAPIRequest $request, $publicId)
2016-01-06 15:01:52 +11:00
{
2016-05-02 16:45:12 +03:00
if ($request->action == ACTION_CONVERT) {
2016-05-29 12:26:02 +03:00
$quote = $request->entity();
$invoice = $this->invoiceRepo->cloneInvoice($quote, $quote->id);
2017-01-30 21:40:43 +02:00
2016-05-02 16:45:12 +03:00
return $this->itemResponse($invoice);
} elseif ($request->action) {
return $this->handleAction($request);
2016-01-28 12:55:15 +11:00
}
$data = $request->input();
$data['public_id'] = $publicId;
$this->invoiceService->save($data, $request->entity());
2016-05-02 16:45:12 +03:00
$invoice = Invoice::scope($publicId)
->with('client', 'invoice_items', 'invitations')
->firstOrFail();
2016-05-29 12:26:02 +03:00
2016-05-02 16:45:12 +03:00
return $this->itemResponse($invoice);
2016-01-06 15:01:52 +11:00
}
2016-01-16 17:07:29 +11:00
2017-01-30 21:40:43 +02:00
/**
* @SWG\Delete(
* path="/invoices/{invoice_id}",
2017-01-30 21:40:43 +02:00
* summary="Delete an invoice",
* tags={"invoice"},
2017-01-30 21:40:43 +02:00
* @SWG\Parameter(
* in="path",
* name="invoice_id",
* type="integer",
* required=true
2017-01-30 21:40:43 +02:00
* ),
* @SWG\Response(
* response=200,
* description="Deleted invoice",
2017-01-30 21:40:43 +02:00
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Invoice"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
2017-03-09 12:25:56 +02:00
public function destroy(UpdateInvoiceAPIRequest $request)
2016-01-16 17:07:29 +11:00
{
2016-05-03 11:53:00 +03:00
$invoice = $request->entity();
2016-05-29 12:26:02 +03:00
2016-01-16 17:07:29 +11:00
$this->invoiceRepo->delete($invoice);
2016-05-03 11:53:00 +03:00
return $this->itemResponse($invoice);
2016-01-16 17:07:29 +11:00
}
public function download(InvoiceRequest $request)
{
$invoice = $request->entity();
2017-08-16 10:52:34 +03:00
if ($invoice->is_deleted) {
abort(404);
}
$pdfString = $invoice->getPDFString();
if ($pdfString) {
return $this->fileReponse($invoice->getFileName(), $pdfString);
} else {
abort(404);
}
}
2015-03-17 07:45:25 +10:00
}