diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php index e0de408ca5e2..de8572747db0 100644 --- a/app/Console/Commands/CreateTestData.php +++ b/app/Console/Commands/CreateTestData.php @@ -483,7 +483,7 @@ class CreateTestData extends Command UpdateInvoicePayment::dispatchNow($payment, $payment->company); } //@todo this slow things down, but gives us PDFs of the invoices for inspection whilst debugging. - //event(new InvoiceWasCreated($invoice, $invoice->company)); + event(new InvoiceWasCreated($invoice, $invoice->company)); } private function createCredit($client) diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 6c4ad4638116..4a9a8a5bae44 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -217,7 +217,7 @@ class CompanySettings extends BaseSettings public $embed_documents = false; public $all_pages_header = true; public $all_pages_footer = true; - + public $invoice_variables = []; public static $casts = [ 'auto_email_invoice' => 'bool', @@ -369,6 +369,7 @@ class CompanySettings extends BaseSettings 'counter_padding' => 'integer', 'design' => 'string', 'website' => 'string', + 'invoice_variables' => 'object', ]; /** @@ -413,7 +414,8 @@ class CompanySettings extends BaseSettings $data->date_format_id = (string)config('ninja.i18n.date_format_id'); $data->country_id = (string)config('ninja.i18n.country_id'); $data->translations = (object) []; - + $data->invoice_variables = (array) self::getInvoiceVariableDefaults(); + // $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject(); // $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate(); // $data->email_subject_quote = EmailTemplateDefaults::emailQuoteSubject(); @@ -453,4 +455,53 @@ class CompanySettings extends BaseSettings return $settings; } + + private static function getInvoiceVariableDefaults() + { + $variables = [ + 'client_details' => [ + 'name', + 'id_number', + 'vat_number', + 'address1', + 'address2', + 'city_state_postal', + 'country', + 'email', + ], + 'company_details' => [ + 'company_name', + 'id_number', + 'vat_number', + 'website', + 'email', + 'phone', + ], + 'company_address' => [ + 'address1', + 'address2', + 'city_state_postal', + 'country', + ], + 'invoice_details' => [ + 'invoice_number', + 'po_number', + 'date', + 'due_date', + 'balance_due', + 'invoice_total', + ], + 'table_columns' => [ + 'product_key', + 'notes', + 'cost', + 'quantity', + 'discount', + 'tax_name1', + 'line_total' + ], + ]; + + return $variables; + } } diff --git a/app/DataMapper/DefaultSettings.php b/app/DataMapper/DefaultSettings.php index b5c8ca2b0a69..44f1241823d1 100644 --- a/app/DataMapper/DefaultSettings.php +++ b/app/DataMapper/DefaultSettings.php @@ -34,7 +34,7 @@ class DefaultSettings extends BaseSettings public static function userSettings() : \stdClass { return (object)[ - class_basename(User::class) => self::userSettingsObject(), + // class_basename(User::class) => self::userSettingsObject(), ]; } @@ -44,7 +44,7 @@ class DefaultSettings extends BaseSettings private static function userSettingsObject() : \stdClass { return (object)[ - 'per_page' => self::$per_page, + // 'per_page' => self::$per_page, ]; } } diff --git a/app/Designs/Designer.php b/app/Designs/Designer.php index b7ab359ab5dd..6a4456f6d904 100644 --- a/app/Designs/Designer.php +++ b/app/Designs/Designer.php @@ -11,6 +11,7 @@ namespace App\Designs; +use App\Models\Company; use App\Models\Invoice; class Designer @@ -24,11 +25,34 @@ class Designer protected $html; - public function __construct($design, array $input_variables) + private static $custom_fields = [ + 'invoice1', + 'invoice2', + 'invoice3', + 'invoice4', + 'surcharge1', + 'surcharge2', + 'surcharge3', + 'surcharge4', + 'client1', + 'client2', + 'client3', + 'client4', + 'contact1', + 'contact2', + 'contact3', + 'contact4', + 'company1', + 'company2', + 'company3', + 'company4', + ]; + + public function __construct($design, $input_variables) { $this->design = $design; - $this->input_variables = $input_variables; + $this->input_variables = (array)$input_variables; } /** @@ -39,7 +63,7 @@ class Designer public function build(Invoice $invoice) :Designer { - $this->exportVariables() + $this->exportVariables($invoice) ->setDesign($this->getSection('header')) ->setDesign($this->getSection('body')) ->setDesign($this->getTable($invoice)) @@ -86,14 +110,15 @@ class Designer return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{$section}()); } - private function exportVariables() + private function exportVariables($invoice) { + $company = $invoice->company; - $this->exported_variables['$client_details'] = $this->processVariables($this->input_variables['client_details'], $this->clientDetails()); - $this->exported_variables['$company_details'] = $this->processVariables($this->input_variables['company_details'], $this->companyDetails()); - $this->exported_variables['$company_address'] = $this->processVariables($this->input_variables['company_address'], $this->companyAddress()); - $this->exported_variables['$invoice_details_labels'] = $this->processLabels($this->input_variables['invoice_details'], $this->invoiceDetails()); - $this->exported_variables['$invoice_details'] = $this->processVariables($this->input_variables['invoice_details'], $this->invoiceDetails()); + $this->exported_variables['$client_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['client_details']), $this->clientDetails($company)); + $this->exported_variables['$company_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_details']), $this->companyDetails($company)); + $this->exported_variables['$company_address'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_address']), $this->companyAddress($company)); + $this->exported_variables['$invoice_details_labels'] = $this->processLabels($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company)); + $this->exported_variables['$invoice_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company)); return $this; } @@ -153,10 +178,10 @@ class Designer // $footer = $this->design->footer(); // } - private function clientDetails() + private function clientDetails(Company $company) { - return [ + $data = [ 'name' => '

$client.name

', 'id_number' => '

$client.id_number

', 'vat_number' => '

$client.vat_number

', @@ -166,51 +191,59 @@ class Designer 'postal_city_state' => '

$client.postal_city_state

', 'country' => '

$client.country

', 'email' => '

$client.email

', - 'custom_value1' => '

$client.custom_value1

', - 'custom_value2' => '

$client.custom_value2

', - 'custom_value3' => '

$client.custom_value3

', - 'custom_value4' => '

$client.custom_value4

', + 'client1' => '

$client1

', + 'client2' => '

$client2

', + 'client3' => '

$client3

', + 'client4' => '

$client4

', + 'contact1' => '

$contact1

', + 'contact2' => '

$contact2

', + 'contact3' => '

$contact3

', + 'contact4' => '

$contact4

', ]; + return $this->processCustomFields($company, $data); } - private function companyDetails() + private function companyDetails(Company $company) { - return [ + $data = [ 'company_name' => '$company.company_name', 'id_number' => '$company.id_number', 'vat_number' => '$company.vat_number', 'website' => '$company.website', 'email' => '$company.email', 'phone' => '$company.phone', - 'custom_value1' => '$company.custom_value1', - 'custom_value2' => '$company.custom_value2', - 'custom_value3' => '$company.custom_value3', - 'custom_value4' => '$company.custom_value4', + 'company1' => '$company1', + 'company2' => '$company2', + 'company3' => '$company3', + 'company4' => '$company4', ]; + + return $this->processCustomFields($company, $data); } - private function companyAddress() + private function companyAddress(Company $company) { - return [ + $data = [ 'address1' => '$company.address1', 'address2' => '$company.address1', 'city_state_postal' => '$company.city_state_postal', 'postal_city_state' => '$company.postal_city_state', 'country' => '$company.country', - 'custom_value1' => '$company.custom_value1', - 'custom_value2' => '$company.custom_value2', - 'custom_value3' => '$company.custom_value3', - 'custom_value4' => '$company.custom_value4', + 'company1' => '$company1', + 'company2' => '$company2', + 'company3' => '$company3', + 'company4' => '$company4', ]; + return $this->processCustomFields($company, $data); } - private function invoiceDetails() + private function invoiceDetails(Company $company) { - return [ + $data = [ 'invoice_number' => '$invoice_number', 'po_number' => '$po_number', 'date' => '$date', @@ -218,11 +251,62 @@ class Designer 'balance_due' => '$balance_due', 'invoice_total' => '$invoice_total', 'partial_due' => '$partial_due', - 'custom_value1' => '$invoice.custom_value1', - 'custom_value2' => '$invoice.custom_value2', - 'custom_value3' => '$invoice.custom_value3', - 'custom_value4' => '$invoice.custom_value4', + 'invoice1' => '$invoice1', + 'invoice2' => '$invoice2', + 'invoice3' => '$invoice3', + 'invoice4' => '$invoice4', + 'surcharge1' =>'$surcharge1', + 'surcharge2' =>'$surcharge2', + 'surcharge3' =>'$surcharge3', + 'surcharge4' =>'$surcharge4', ]; + + return $this->processCustomFields($company, $data); + } + + private function processCustomFields(Company $company, $data) + { + + $custom_fields = $company->custom_fields; + + foreach(self::$custom_fields as $cf) + { + + if(!property_exists($custom_fields, $cf) || (strlen($custom_fields->{$cf}) == 0)) + unset($data[$cf]); + + } + + return $data; + + } + + private function processInputVariables($company, $variables) + { + + $custom_fields = $company->custom_fields; + + $matches = array_intersect(self::$custom_fields, $variables); + + foreach($matches as $match) + { + + if(!property_exists($custom_fields, $match) || (strlen($custom_fields->{$match}) == 0)) + { + foreach($variables as $key => $value) + { + if($value == $match) + { + unset($variables[$key]); + } + } + } + + + } + + return $variables; + } } \ No newline at end of file diff --git a/app/Designs/Modern.php b/app/Designs/Modern.php index 815116373caa..11091cc1990b 100644 --- a/app/Designs/Modern.php +++ b/app/Designs/Modern.php @@ -30,6 +30,7 @@ class Modern @@ -98,11 +99,11 @@ class Modern -
+
$invoice.public_notes
-
+
$total_tax_labels $line_tax_labels diff --git a/app/Factory/CompanyFactory.php b/app/Factory/CompanyFactory.php index 03b84ac41b49..0dfe4f4ed20e 100644 --- a/app/Factory/CompanyFactory.php +++ b/app/Factory/CompanyFactory.php @@ -31,7 +31,8 @@ class CompanyFactory $company->company_key = $this->createHash(); $company->settings = CompanySettings::defaults(); $company->db = config('database.default'); - $company->custom_fields = (object) ['custom1' => '1', 'custom2' => '2', 'custom3'=>'3']; + //$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3']; + $company->custom_fields = (object) []; $company->subdomain = ''; return $company; diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 17ee43a6c82f..f32a609adc59 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -56,32 +56,35 @@ class BaseController extends Controller public function __construct() { + $this->manager = new Manager(); $this->forced_includes = []; $this->forced_index = 'data'; + } private function buildManager() { + $include = ''; if(request()->has('first_load') && request()->input('first_load') == 'true') { - $include = $this->getRequestIncludes([]); - $include = array_merge($this->forced_includes, $include); + $include = implode("," , array_merge($this->forced_includes, $this->getRequestIncludes([]))); - $include = implode(",", $include); } else if (request()->input('include') !== null) { - $request_include = explode(",", request()->input('include')); - $include = array_merge($this->forced_includes, $request_include); + $include = array_merge($this->forced_includes, explode(",", request()->input('include'))); $include = implode(",", $include); + } elseif (count($this->forced_includes) >= 1) { + $include = implode(",", $this->forced_includes); + } $this->manager->parseIncludes($include); @@ -89,10 +92,15 @@ class BaseController extends Controller $this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY; if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) { + $this->manager->setSerializer(new JsonApiSerializer()); + } else { + $this->manager->setSerializer(new ArraySerializer()); + } + } /** @@ -101,27 +109,36 @@ class BaseController extends Controller */ public function notFound() { + return response()->json(['message' => '404 | Nothing to see here!'], 404) ->header('X-API-VERSION', config('ninja.api_version')) ->header('X-APP-VERSION', config('ninja.app_version')); + } public function notFoundClient() { + return abort(404); + } protected function errorResponse($response, $httpErrorCode = 400) { + $error['error'] = $response; + $error = json_encode($error, JSON_PRETTY_PRINT); + $headers = self::getApiHeaders(); return response()->make($error, $httpErrorCode, $headers); + } protected function listResponse($query) { + $this->buildManager(); $transformer = new $this->entity_transformer(Input::get('serializer')); @@ -145,32 +162,40 @@ class BaseController extends Controller $data = $this->createCollection($query, $transformer, $this->entity_type); return $this->response($data); + } protected function createCollection($query, $transformer, $entity_type) { + $this->buildManager(); - if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON) { + if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON) $entity_type = null; - } + if (is_a($query, "Illuminate\Database\Eloquent\Builder")) { + $limit = Input::get('per_page', 20); $paginator = $query->paginate($limit); $query = $paginator->getCollection(); $resource = new Collection($query, $transformer, $entity_type); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + } else { + $resource = new Collection($query, $transformer, $entity_type); + } return $this->manager->createData($resource)->toArray(); + } protected function response($response) { + $index = request()->input('index') ?: $this->forced_index; if ($index == 'none') { @@ -194,55 +219,50 @@ class BaseController extends Controller ksort($response); $response = json_encode($response, JSON_PRETTY_PRINT); + $headers = self::getApiHeaders(); - return response()->make($response, 200, $headers); + } protected function itemResponse($item) { + $this->buildManager(); $transformer = new $this->entity_transformer(Input::get('serializer')); $data = $this->createItem($item, $transformer, $this->entity_type); - if (request()->include_static) { + if (request()->include_static) $data['static'] = Statics::company(auth()->user()->getCompany()->getLocale()); - } - return $this->response($data); + } protected function createItem($data, $transformer, $entity_type) { - if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON) { - $entity_type = null; - } - + if ($this->serializer && $this->serializer != EntityTransformer::API_SERIALIZER_JSON) + $entity_type = null; + $resource = new Item($data, $transformer, $entity_type); return $this->manager->createData($resource)->toArray(); + } public static function getApiHeaders($count = 0) { + return [ 'Content-Type' => 'application/json', - //'Access-Control-Allow-Origin' => '*', - //'Access-Control-Allow-Methods' => 'GET', - //'Access-Control-Allow-Headers' => 'Origin, Content-Type, Accept, Authorization, X-Requested-With', - //'Access-Control-Allow-Credentials' => 'true', - //'X-Total-Count' => $count, 'X-Api-Version' => config('ninja.api_version'), 'X-App-Version' => config('ninja.app_version'), - //'X-Rate-Limit-Limit' - The number of allowed requests in the current period - //'X-Rate-Limit-Remaining' - The number of remaining requests in the current period - //'X-Rate-Limit-Reset' - The number of seconds left in the current period, ]; + } @@ -279,7 +299,9 @@ class BaseController extends Controller 'company.groups', ]; - + /** + * Thresholds for displaying large account on first load + */ if (request()->has('first_load') && request()->input('first_load') == 'true') { @@ -315,4 +337,5 @@ class BaseController extends Controller return $data; } + } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index fdb423150a5c..50319d2ca668 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -24,640 +24,636 @@ use App\Http\Requests\Invoice\EditInvoiceRequest; use App\Http\Requests\Invoice\ShowInvoiceRequest; use App\Http\Requests\Invoice\StoreInvoiceRequest; use App\Http\Requests\Invoice\UpdateInvoiceRequest; -use App\Jobs\Entity\ActionEntity; + use App\Jobs\Invoice\EmailInvoice; -use App\Jobs\Invoice\MarkInvoicePaid; + use App\Jobs\Invoice\StoreInvoice; use App\Models\Invoice; -use App\Repositories\BaseRepository; + use App\Repositories\InvoiceRepository; use App\Transformers\InvoiceTransformer; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Log; /** * Class InvoiceController * @package App\Http\Controllers\InvoiceController */ -class InvoiceController extends BaseController -{ - use MakesHash; +class InvoiceController extends BaseController { + use MakesHash; - protected $entity_type = Invoice::class; + protected $entity_type = Invoice::class ; - protected $entity_transformer = InvoiceTransformer::class; + protected $entity_transformer = InvoiceTransformer::class ; - /** - * @var InvoiceRepository - */ - protected $invoice_repo; + /** + * @var InvoiceRepository + */ + protected $invoice_repo; - /** - * InvoiceController constructor. - * - * @param \App\Repositories\InvoiceRepository $invoice_repo The invoice repo - */ - public function __construct(InvoiceRepository $invoice_repo) - { - parent::__construct(); + /** + * InvoiceController constructor. + * + * @param \App\Repositories\InvoiceRepository $invoice_repo The invoice repo + */ + public function __construct(InvoiceRepository $invoice_repo) { + parent::__construct(); - $this->invoice_repo = $invoice_repo; - } + $this->invoice_repo = $invoice_repo; + } - /** - * Show the list of Invoices - * - * @param \App\Filters\InvoiceFilters $filters The filters - * - * @return \Illuminate\Http\Response - * - * @OA\Get( - * path="/api/v1/invoices", - * operationId="getInvoices", - * tags={"invoices"}, - * summary="Gets a list of invoices", - * description="Lists invoices, search and filters allow fine grained lists to be generated. + /** + * Show the list of Invoices + * + * @param \App\Filters\InvoiceFilters $filters The filters + * + * @return \Illuminate\Http\Response + * + * @OA\Get( + * path="/api/v1/invoices", + * operationId="getInvoices", + * tags={"invoices"}, + * summary="Gets a list of invoices", + * description="Lists invoices, search and filters allow fine grained lists to be generated. - Query parameters can be added to performed more fine grained filtering of the invoices, these are handled by the InvoiceFilters class which defines the methods available", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Response( - * response=200, - * description="A list of invoices", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + Query parameters can be added to performed more fine grained filtering of the invoices, these are handled by the InvoiceFilters class which defines the methods available", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="A list of invoices", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function index(InvoiceFilters $filters) - { - $invoices = Invoice::filter($filters); - - return $this->listResponse($invoices); - } + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function index(InvoiceFilters $filters) { + $invoices = Invoice::filter($filters); - /** - * Show the form for creating a new resource. - * - * @param \App\Http\Requests\Invoice\CreateInvoiceRequest $request The request - * - * @return \Illuminate\Http\Response - * - * - * @OA\Get( - * path="/api/v1/invoices/create", - * operationId="getInvoicesCreate", - * tags={"invoices"}, - * summary="Gets a new blank invoice object", - * description="Returns a blank object with default values", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Response( - * response=200, - * description="A blank invoice object", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function create(CreateInvoiceRequest $request) - { - $invoice = InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id); + return $this->listResponse($invoices); + } - return $this->itemResponse($invoice); - } + /** + * Show the form for creating a new resource. + * + * @param \App\Http\Requests\Invoice\CreateInvoiceRequest $request The request + * + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/invoices/create", + * operationId="getInvoicesCreate", + * tags={"invoices"}, + * summary="Gets a new blank invoice object", + * description="Returns a blank object with default values", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="A blank invoice object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function create(CreateInvoiceRequest $request) { + $invoice = InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id); + return $this->itemResponse($invoice); + } - /** - * Store a newly created resource in storage. - * - * @param \App\Http\Requests\Invoice\StoreInvoiceRequest $request The request - * - * @return \Illuminate\Http\Response - * - * - * @OA\Post( - * path="/api/v1/invoices", - * operationId="storeInvoice", - * tags={"invoices"}, - * summary="Adds a invoice", - * description="Adds an invoice to the system", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Response( - * response=200, - * description="Returns the saved invoice object", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function store(StoreInvoiceRequest $request) - { - $invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id)); + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\Invoice\StoreInvoiceRequest $request The request + * + * @return \Illuminate\Http\Response + * + * + * @OA\Post( + * path="/api/v1/invoices", + * operationId="storeInvoice", + * tags={"invoices"}, + * summary="Adds a invoice", + * description="Adds an invoice to the system", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="Returns the saved invoice object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function store(StoreInvoiceRequest $request) { + $invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id)); - $invoice = StoreInvoice::dispatchNow($invoice, $request->all(), $invoice->company); //todo potentially this may return mixed ie PDF/$invoice... need to revisit when we implement UI + $invoice = StoreInvoice::dispatchNow($invoice, $request->all(), $invoice->company);//todo potentially this may return mixed ie PDF/$invoice... need to revisit when we implement UI - event(new InvoiceWasCreated($invoice, $invoice->company)); + event(new InvoiceWasCreated($invoice, $invoice->company)); - return $this->itemResponse($invoice); - } + return $this->itemResponse($invoice); + } - /** - * Display the specified resource. - * - * @param \App\Http\Requests\Invoice\ShowInvoiceRequest $request The request - * @param \App\Models\Invoice $invoice The invoice - * - * @return \Illuminate\Http\Response - * - * - * @OA\Get( - * path="/api/v1/invoices/{id}", - * operationId="showInvoice", - * tags={"invoices"}, - * summary="Shows an invoice", - * description="Displays an invoice by id", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Invoice Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the invoice object", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function show(ShowInvoiceRequest $request, Invoice $invoice) - { - return $this->itemResponse($invoice); - } + /** + * Display the specified resource. + * + * @param \App\Http\Requests\Invoice\ShowInvoiceRequest $request The request + * @param \App\Models\Invoice $invoice The invoice + * + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/invoices/{id}", + * operationId="showInvoice", + * tags={"invoices"}, + * summary="Shows an invoice", + * description="Displays an invoice by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Invoice Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the invoice object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function show(ShowInvoiceRequest $request, Invoice $invoice) { + return $this->itemResponse($invoice); + } - /** - * Show the form for editing the specified resource. - * - * @param \App\Http\Requests\Invoice\EditInvoiceRequest $request The request - * @param \App\Models\Invoice $invoice The invoice - * - * @return \Illuminate\Http\Response - * - * @OA\Get( - * path="/api/v1/invoices/{id}/edit", - * operationId="editInvoice", - * tags={"invoices"}, - * summary="Shows an invoice for editting", - * description="Displays an invoice by id", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Invoice Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the invoice object", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function edit(EditInvoiceRequest $request, Invoice $invoice) - { - return $this->itemResponse($invoice); - } - - /** - * Update the specified resource in storage. - * - * @param \App\Http\Requests\Invoice\UpdateInvoiceRequest $request The request - * @param \App\Models\Invoice $invoice The invoice - * - * @return \Illuminate\Http\Response - * - * - * @OA\Put( - * path="/api/v1/invoices/{id}", - * operationId="updateInvoice", - * tags={"invoices"}, - * summary="Updates an invoice", - * description="Handles the updating of an invoice by id", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Invoice Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the invoice object", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function update(UpdateInvoiceRequest $request, Invoice $invoice) - { - if($request->entityIsDeleted($invoice)) - return $request->disallowUpdate(); + /** + * Show the form for editing the specified resource. + * + * @param \App\Http\Requests\Invoice\EditInvoiceRequest $request The request + * @param \App\Models\Invoice $invoice The invoice + * + * @return \Illuminate\Http\Response + * + * @OA\Get( + * path="/api/v1/invoices/{id}/edit", + * operationId="editInvoice", + * tags={"invoices"}, + * summary="Shows an invoice for editting", + * description="Displays an invoice by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Invoice Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the invoice object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function edit(EditInvoiceRequest $request, Invoice $invoice) { + return $this->itemResponse($invoice); + } - $invoice = $this->invoice_repo->save($request->all(), $invoice); + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\Invoice\UpdateInvoiceRequest $request The request + * @param \App\Models\Invoice $invoice The invoice + * + * @return \Illuminate\Http\Response + * + * + * @OA\Put( + * path="/api/v1/invoices/{id}", + * operationId="updateInvoice", + * tags={"invoices"}, + * summary="Updates an invoice", + * description="Handles the updating of an invoice by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Invoice Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the invoice object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function update(UpdateInvoiceRequest $request, Invoice $invoice) { + if ($request->entityIsDeleted($invoice)) { + return $request->disallowUpdate(); + } - event(new InvoiceWasUpdated($invoice, $invoice->company)); + $invoice = $this->invoice_repo->save($request->all(), $invoice); - return $this->itemResponse($invoice); - } + event(new InvoiceWasUpdated($invoice, $invoice->company)); - /** - * Remove the specified resource from storage. - * - * @param \App\Http\Requests\Invoice\DestroyInvoiceRequest $request - * @param \App\Models\Invoice $invoice - * - * @return \Illuminate\Http\Response - * - * @OA\Delete( - * path="/api/v1/invoices/{id}", - * operationId="deleteInvoice", - * tags={"invoices"}, - * summary="Deletes a invoice", - * description="Handles the deletion of an invoice by id", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Invoice Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns a HTTP status", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function destroy(DestroyInvoiceRequest $request, Invoice $invoice) - { - $invoice->delete(); + return $this->itemResponse($invoice); + } - return response()->json([], 200); - } + /** + * Remove the specified resource from storage. + * + * @param \App\Http\Requests\Invoice\DestroyInvoiceRequest $request + * @param \App\Models\Invoice $invoice + * + * @return \Illuminate\Http\Response + * + * @OA\Delete( + * path="/api/v1/invoices/{id}", + * operationId="deleteInvoice", + * tags={"invoices"}, + * summary="Deletes a invoice", + * description="Handles the deletion of an invoice by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Invoice Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns a HTTP status", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function destroy(DestroyInvoiceRequest $request, Invoice $invoice) { + $invoice->delete(); - /** - * Perform bulk actions on the list view - * - * @return Collection - * - * @OA\Post( - * path="/api/v1/invoices/bulk", - * operationId="bulkInvoices", - * tags={"invoices"}, - * summary="Performs bulk actions on an array of invoices", - * description="", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/index"), - * @OA\RequestBody( - * description="User credentials", - * required=true, - * @OA\MediaType( - * mediaType="application/json", - * @OA\Schema( - * type="array", - * @OA\Items( - * type="integer", - * description="Array of hashed IDs to be bulk 'actioned", - * example="[0,1,2,3]", - * ), - * ) - * ) - * ), - * @OA\Response( - * response=200, - * description="The Company User response", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/CompanyUser"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + return response()->json([], 200); + } - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function bulk() - { - $action = request()->input('action'); - - $ids = request()->input('ids'); + /** + * Perform bulk actions on the list view + * + * @return Collection + * + * @OA\Post( + * path="/api/v1/invoices/bulk", + * operationId="bulkInvoices", + * tags={"invoices"}, + * summary="Performs bulk actions on an array of invoices", + * description="", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/index"), + * @OA\RequestBody( + * description="User credentials", + * required=true, + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * type="array", + * @OA\Items( + * type="integer", + * description="Array of hashed IDs to be bulk 'actioned", + * example="[0,1,2,3]", + * ), + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="The Company User response", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/CompanyUser"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - $invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids)); + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function bulk() { + $action = request()->input('action'); - if (!$invoices) { - return response()->json(['message'=>'No Invoices Found']); - } + $ids = request()->input('ids'); - $invoices->each(function ($invoice, $key) use ($action) { + $invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids)); -// $this->invoice_repo->{$action}($invoice); + if (!$invoices) { + return response()->json(['message' => 'No Invoices Found']); + } - if (auth()->user()->can('edit', $invoice)) { - $this->performAction($invoice, $action, true); - } - }); + $invoices->each(function ($invoice, $key) use ($action) { - return $this->listResponse(Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))); - } + // $this->invoice_repo->{$action}($invoice); - /** - * - * @OA\Get( - * path="/api/v1/invoices/{id}/{action}", - * operationId="actionInvoice", - * tags={"invoices"}, - * summary="Performs a custom action on an invoice", - * description="Performs a custom action on an invoice. + if (auth()->user()->can('edit', $invoice)) { + $this->performAction($invoice, $action, true); + } + }); - The current range of actions are as follows - - clone_to_invoice - - clone_to_quote - - history - - delivery_note - - mark_paid - - download - - archive - - delete - - email", - * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), - * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Invoice Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Parameter( - * name="action", - * in="path", - * description="The action string to be performed", - * example="clone_to_quote", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the invoice object", - * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Invoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - * - */ - public function action(ActionInvoiceRequest $request, Invoice $invoice, $action) - { - return $this->performAction($invoice, $action); - } - - private function performAction(Invoice $invoice, $action, $bulk = false) - { - /*If we are using bulk actions, we don't want to return anything */ - switch ($action) { - case 'clone_to_invoice': - $invoice = CloneInvoiceFactory::create($invoice, auth()->user()->id); - return $this->itemResponse($invoice); - break; - case 'clone_to_quote': - $quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id); - // todo build the quote transformer and return response here - break; - case 'history': - # code... - break; - case 'delivery_note': - # code... - break; - case 'mark_paid': - if ($invoice->balance < 0 || $invoice->status_id == Invoice::STATUS_PAID || $invoice->is_deleted === true) { - return $this->errorResponse(['message' => 'Invoice cannot be marked as paid'], 400); - } + return $this->listResponse(Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))); + } - $invoice = $invoice->service()->markPaid(); + /** + * + * @OA\Get( + * path="/api/v1/invoices/{id}/{action}", + * operationId="actionInvoice", + * tags={"invoices"}, + * summary="Performs a custom action on an invoice", + * description="Performs a custom action on an invoice. - if (!$bulk) { - return $this->itemResponse($invoice); - } - break; - case 'mark_sent': - $invoice->service()->markSent()->save(); + The current range of actions are as follows + - clone_to_invoice + - clone_to_quote + - history + - delivery_note + - mark_paid + - download + - archive + - delete + - email", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Invoice Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Parameter( + * name="action", + * in="path", + * description="The action string to be performed", + * example="clone_to_quote", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the invoice object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function action(ActionInvoiceRequest $request, Invoice $invoice, $action) { + return $this->performAction($invoice, $action); + } - if (!$bulk) { - return $this->itemResponse($invoice); - } - break; - case 'download': - return response()->download(public_path($invoice->pdf_file_path())); - break; - case 'archive': - $this->invoice_repo->archive($invoice); + private function performAction(Invoice $invoice, $action, $bulk = false) { + /*If we are using bulk actions, we don't want to return anything */ + switch ($action) { + case 'clone_to_invoice': + $invoice = CloneInvoiceFactory::create($invoice, auth()->user()->id); + return $this->itemResponse($invoice); + break; + case 'clone_to_quote': + $quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id); + // todo build the quote transformer and return response here + break; + case 'history': + # code... + break; + case 'delivery_note': + # code... + break; + case 'mark_paid': + if ($invoice->balance < 0 || $invoice->status_id == Invoice::STATUS_PAID || $invoice->is_deleted === true) { + return $this->errorResponse(['message' => 'Invoice cannot be marked as paid'], 400); + } - if (!$bulk) { - return $this->listResponse($invoice); - } - break; - case 'delete': - $this->invoice_repo->delete($invoice); + $invoice = $invoice->service()->markPaid(); - if (!$bulk) { - return $this->listResponse($invoice); - } - break; - case 'email': - EmailInvoice::dispatch($invoice, $invoice->company); - if (!$bulk) { - return response()->json(['message'=>'email sent'], 200); - } - break; + if (!$bulk) { + return $this->itemResponse($invoice); + } + break; + case 'mark_sent': + $invoice->service()->markSent()->save(); - default: - return response()->json(['message' => "The requested action `{$action}` is not available."], 400); - break; - } - } + if (!$bulk) { + return $this->itemResponse($invoice); + } + break; + case 'download': + return response()->download(public_path($invoice->pdf_file_path())); + break; + case 'archive': + $this->invoice_repo->archive($invoice); + + if (!$bulk) { + return $this->listResponse($invoice); + } + break; + case 'delete': + $this->invoice_repo->delete($invoice); + + if (!$bulk) { + return $this->listResponse($invoice); + } + break; + case 'email': + EmailInvoice::dispatch($invoice, $invoice->company); + if (!$bulk) { + return response()->json(['message' => 'email sent'], 200); + } + break; + + default: + return response()->json(['message' => "The requested action `{$action}` is not available."], 400); + break; + } + } + + public function downloadPdf($invitation_key) { + + $invitation = InvoiceInvitation::whereKey($invitation_key)->company()->first(); + $contact = $invitation->contact; + $invoice = $invitation->invoice; + + return response()->json($invitation_key); + } } diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index 24bca36f1713..e8cccd980e33 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -11,6 +11,9 @@ namespace App\Http\Controllers; +use Codedge\Updater\UpdaterManager; +use Illuminate\Foundation\Bus\DispatchesJobs; + class SelfUpdateController extends BaseController { use DispatchesJobs; @@ -20,8 +23,10 @@ class SelfUpdateController extends BaseController } - public function update() + public function update(UpdaterManager $updater) { + + $updater->update(); } } diff --git a/app/Http/Controllers/TemplateController.php b/app/Http/Controllers/TemplateController.php index f5945a24d537..a979f92e13bd 100644 --- a/app/Http/Controllers/TemplateController.php +++ b/app/Http/Controllers/TemplateController.php @@ -103,8 +103,8 @@ class TemplateController extends BaseController $entity_obj = $class::whereId(request()->input('entity_id'))->company()->first(); } - $subject = request()->input('subject'); - $body = request()->input('body'); + $subject = request()->input('subject') ?: ''; + $body = request()->input('body') ?: ''; $converter = new CommonMarkConverter([ 'html_input' => 'strip', diff --git a/app/Jobs/Invoice/CreateInvoicePdf.php b/app/Jobs/Invoice/CreateInvoicePdf.php index e178402e4cde..4891f64aacd7 100644 --- a/app/Jobs/Invoice/CreateInvoicePdf.php +++ b/app/Jobs/Invoice/CreateInvoicePdf.php @@ -14,11 +14,10 @@ namespace App\Jobs\Invoice; use App\Designs\Designer; use App\Designs\Modern; use App\Libraries\MultiDB; +use App\Models\ClientContact; use App\Models\Company; use App\Models\Invoice; -use App\Models\Payment; -use App\Models\PaymentTerm; -use App\Repositories\InvoiceRepository; + use App\Utils\Traits\MakesInvoiceHtml; use App\Utils\Traits\NumberFormatter; use Illuminate\Bus\Queueable; @@ -26,142 +25,78 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\Log; + +use Illuminate\Support\Facades\App; + use Illuminate\Support\Facades\Storage; use Spatie\Browsershot\Browsershot; -use Symfony\Component\Debug\Exception\FatalThrowableError; -class CreateInvoicePdf implements ShouldQueue -{ - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml; +class CreateInvoicePdf implements ShouldQueue { + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml; - public $invoice; + public $invoice; - public $company; - /** - * Create a new job instance. - * - * @return void - */ - public function __construct(Invoice $invoice, Company $company) - { - $this->invoice = $invoice; + public $company; - $this->company = $company; - } + public $contact; + /** + * Create a new job instance. + * + * @return void + */ + public function __construct(Invoice $invoice, Company $company, ClientContact $contact) { + $this->invoice = $invoice; - public function handle() - { - MultiDB::setDB($this->company->db); + $this->company = $company; + $this->contact = $contact; + } - $input_variables = [ - 'client_details' => [ - 'name', - 'id_number', - 'vat_number', - 'address1', - 'address2', - 'city_state_postal', - 'postal_city_state', - 'country', - 'email', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', - ], - 'company_details' => [ - 'company_name', - 'id_number', - 'vat_number', - 'website', - 'email', - 'phone', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', - ], - 'company_address' => [ - 'address1', - 'address2', - 'city_state_postal', - 'postal_city_state', - 'country', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', - ], - 'invoice_details' => [ - 'invoice_number', - 'po_number', - 'date', - 'due_date', - 'balance_due', - 'invoice_total', - 'partial_due', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', - ], - 'table_columns' => [ - 'product_key', - 'notes', - 'cost', - 'quantity', - 'discount', - 'tax_name1', - 'line_total' - ], - ]; + public function handle() { + MultiDB::setDB($this->company->db); + App::setLocale($this->contact->preferredLocale()); + $this->invoice->load('client'); + $path = 'public/'.$this->invoice->client->client_hash.'/invoices/'; + $file_path = $path.$this->invoice->number.'.pdf'; + $modern = new Modern(); + $designer = new Designer($modern, $this->invoice->client->getSetting('invoice_variables')); + //get invoice design + $html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice, $this->contact); - $this->invoice->load('client'); - $path = 'public/' . $this->invoice->client->client_hash . '/invoices/'; - $file_path = $path . $this->invoice->number . '.pdf'; + //todo - move this to the client creation stage so we don't keep hitting this unnecessarily + Storage::makeDirectory($path, 0755); - $modern = new Modern(); - $designer = new Designer($modern, $input_variables); + //\Log::error($html); + //create pdf + $pdf = $this->makePdf(null, null, $html); - //get invoice design - $html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice); + $path = Storage::put($file_path, $pdf); - //todo - move this to the client creation stage so we don't keep hitting this unnecessarily - Storage::makeDirectory($path, 0755); + return $path; + } -\Log::error($html); - //create pdf - $pdf = $this->makePdf(null, null, $html); - - $path = Storage::put($file_path, $pdf); - } - - /** - * Returns a PDF stream - * - * @param string $header Header to be included in PDF - * @param string $footer Footer to be included in PDF - * @param string $html The HTML object to be converted into PDF - * - * @return string The PDF string - */ - private function makePdf($header, $footer, $html) - { - return Browsershot::html($html) - //->showBrowserHeaderAndFooter() - //->headerHtml($header) - //->footerHtml($footer) - ->deviceScaleFactor(1) - ->showBackground() - ->waitUntilNetworkIdle(false)->pdf(); - //->margins(10,10,10,10) - //->savePdf('test.pdf'); - } + /** + * Returns a PDF stream + * + * @param string $header Header to be included in PDF + * @param string $footer Footer to be included in PDF + * @param string $html The HTML object to be converted into PDF + * + * @return string The PDF string + */ + private function makePdf($header, $footer, $html) { + return Browsershot::html($html) + //->showBrowserHeaderAndFooter() + //->headerHtml($header) + //->footerHtml($footer) + ->deviceScaleFactor(1) + ->showBackground() + ->waitUntilNetworkIdle(true) ->pdf(); + //->margins(10,10,10,10) + //->savePdf('test.pdf'); + } } diff --git a/app/Listeners/Invoice/CreateInvoicePdf.php b/app/Listeners/Invoice/CreateInvoicePdf.php index af88e7c575a0..6a382c057165 100644 --- a/app/Listeners/Invoice/CreateInvoicePdf.php +++ b/app/Listeners/Invoice/CreateInvoicePdf.php @@ -35,6 +35,6 @@ class CreateInvoicePdf implements ShouldQueue */ public function handle($event) { - PdfCreator::dispatch($event->invoice, $event->company); + PdfCreator::dispatch($event->invoice, $event->company, $event->invoice->client->primary_contact()->first()); } } diff --git a/app/Models/Client.php b/app/Models/Client.php index cdec1f1f1767..8dfecab7a40a 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -32,13 +32,14 @@ use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesHash; use Hashids\Hashids; +use Illuminate\Contracts\Translation\HasLocalePreference; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\URL; use Laracasts\Presenter\PresentableTrait; -class Client extends BaseModel +class Client extends BaseModel implements HasLocalePreference { use PresentableTrait; use MakesHash; @@ -424,4 +425,18 @@ class Client extends BaseModel return $payment_urls; } + + public function preferredLocale() + { + $languages = Cache::get('languages'); + + return $languages->filter(function ($item) { + return $item->id == $this->client->getSetting('language_id'); + })->first()->locale; + + //$lang = Language::find($this->client->getSetting('language_id')); + + //return $lang->locale; + } + } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ea6536f44518..dce27036dcc9 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -33,7 +33,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\File; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Laracasts\Presenter\PresentableTrait; @@ -349,7 +348,7 @@ class Invoice extends BaseModel if (!Storage::exists($storage_path)) { event(new InvoiceWasUpdated($this, $this->company)); - CreateInvoicePdf::dispatch($this, $this->company); + CreateInvoicePdf::dispatch($this, $this->company, $this->client->primary_contact()->first()); } return $public_path; @@ -360,7 +359,7 @@ class Invoice extends BaseModel $storage_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf'; if (!Storage::exists($storage_path)) { - CreateInvoicePdf::dispatchNow($this, $this->company); + CreateInvoicePdf::dispatchNow($this, $this->company, $this->client->primary_contact()->first()); } return $storage_path; diff --git a/app/Models/InvoiceInvitation.php b/app/Models/InvoiceInvitation.php index beba5722eaa6..c23e35318fa7 100644 --- a/app/Models/InvoiceInvitation.php +++ b/app/Models/InvoiceInvitation.php @@ -14,79 +14,70 @@ namespace App\Models; use App\Models\Invoice; use App\Utils\Traits\Inviteable; use App\Utils\Traits\MakesDates; -use Illuminate\Database\Eloquent\Model; + use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; -class InvoiceInvitation extends BaseModel -{ - use MakesDates; - use SoftDeletes; - use Inviteable; +class InvoiceInvitation extends BaseModel { + use MakesDates; + use SoftDeletes; + use Inviteable; - protected $fillable = [ - 'id', - 'client_contact_id', - ]; + protected $fillable = [ + //'id', + //'client_contact_id', + ]; - protected $with = [ - // 'company', - ]; + protected $with = [ + // 'company', + ]; - public function entityType() - { - return Invoice::class; - } + public function entityType() { + return Invoice::class ; + } - /** - * @return mixed - */ - public function invoice() - { - return $this->belongsTo(Invoice::class)->withTrashed(); - } + /** + * @return mixed + */ + public function invoice() { + return $this->belongsTo(Invoice::class )->withTrashed(); + } - /** - * @return mixed - */ - public function contact() - { - return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed(); - } + /** + * @return mixed + */ + public function contact() { + return $this->belongsTo(ClientContact::class , 'client_contact_id', 'id')->withTrashed(); + } - /** - * @return mixed - */ - public function user() - { - return $this->belongsTo(User::class)->withTrashed(); - } + /** + * @return mixed + */ + public function user() { + return $this->belongsTo(User::class )->withTrashed(); + } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function company() - { - return $this->belongsTo(Company::class); - } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function company() { + return $this->belongsTo(Company::class ); + } - public function signatureDiv() - { - if (! $this->signature_base64) { - return false; - } + public function signatureDiv() { + if (!$this->signature_base64) { + return false; + } - return sprintf('

%s: %s', $this->signature_base64, ctrans('texts.signed'), $this->createClientDate($this->signature_date, $this->contact->client->timezone()->name)); - } + return sprintf('

%s: %s', $this->signature_base64, ctrans('texts.signed'), $this->createClientDate($this->signature_date, $this->contact->client->timezone()->name)); + } - public function getName() - { - return $this->key; - } + public function getName() { + return $this->key; + } - public function markViewed() - { - $this->viewed_date = Carbon::now(); - $this->save(); - } + public function markViewed() { + $this->viewed_date = Carbon::now(); + $this->save(); + } } diff --git a/app/Repositories/InvoiceRepository.php b/app/Repositories/InvoiceRepository.php index ee7079651a61..867119cc7b02 100644 --- a/app/Repositories/InvoiceRepository.php +++ b/app/Repositories/InvoiceRepository.php @@ -11,126 +11,119 @@ namespace App\Repositories; -use App\Events\Invoice\InvoiceWasCreated; -use App\Events\Invoice\InvoiceWasUpdated; use App\Factory\InvoiceInvitationFactory; -use App\Helpers\Invoice\InvoiceSum; + use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; use App\Jobs\Product\UpdateOrCreateProduct; -use App\Listeners\Invoice\CreateInvoiceInvitation; + use App\Models\ClientContact; use App\Models\Invoice; use App\Models\InvoiceInvitation; use App\Utils\Traits\MakesHash; -use Illuminate\Http\Request; -use Illuminate\Support\Carbon; /** * InvoiceRepository */ -class InvoiceRepository extends BaseRepository -{ - use MakesHash; - - /** - * Gets the class name. - * - * @return string The class name. - */ - public function getClassName() - { - return Invoice::class; - } - - /** - * Saves the invoices - * - * @param array. $data The invoice data - * @param InvoiceSum|\App\Models\Invoice $invoice The invoice - * - * @return Invoice|InvoiceSum|\App\Models\Invoice|null Returns the invoice object - */ - public function save($data, Invoice $invoice) : ?Invoice - { - /* Always carry forward the initial invoice amount this is important for tracking client balance changes later......*/ - $starting_amount = $invoice->amount; +class InvoiceRepository extends BaseRepository { + use MakesHash; - $invoice->fill($data); + /** + * Gets the class name. + * + * @return string The class name. + */ + public function getClassName() { + return Invoice::class ; + } - $invoice->save(); + /** + * Saves the invoices + * + * @param array. $data The invoice data + * @param InvoiceSum|\App\Models\Invoice $invoice The invoice + * + * @return Invoice|InvoiceSum|\App\Models\Invoice|null Returns the invoice object + */ + public function save($data, Invoice $invoice):?Invoice { - if (isset($data['client_contacts'])) { - foreach ($data['client_contacts'] as $contact) { - if ($contact['send_invoice'] == 1) { - $client_contact = ClientContact::find($this->decodePrimaryKey($contact['id'])); - $client_contact->send_invoice = true; - $client_contact->save(); - } - } - } + /* Always carry forward the initial invoice amount this is important for tracking client balance changes later......*/ + $starting_amount = $invoice->amount; - if (isset($data['invitations'])) { - $invitations = collect($data['invitations']); + $invoice->fill($data); - /* Get array of Keyss which have been removed from the invitations array and soft delete each invitation */ - collect($invoice->invitations->pluck('key'))->diff($invitations->pluck('key'))->each(function ($invitation) { - InvoiceInvitation::destroy($invitation); - }); + $invoice->save(); - foreach ($data['invitations'] as $invitation) { - $inv = false; + if (isset($data['client_contacts'])) { + foreach ($data['client_contacts'] as $contact) { + if ($contact['send_invoice'] == 1) { + $client_contact = ClientContact::find($this->decodePrimaryKey($contact['id'])); + $client_contact->send_invoice = true; + $client_contact->save(); + } + } + } - if (array_key_exists('key', $invitation)) { - $inv = InvoiceInvitation::whereKey($invitation['key'])->first(); - } + if (isset($data['invitations'])) { + $invitations = collect($data['invitations']); - if (!$inv) { + /* Get array of Keyss which have been removed from the invitations array and soft delete each invitation */ + collect($invoice->invitations->pluck('key'))->diff($invitations->pluck('key'))->each(function ($invitation) { + InvoiceInvitation::destroy($invitation); + }); - $new_invitation = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); - $new_invitation->fill($invitation); - $new_invitation->invoice_id = $invoice->id; - $new_invitation->client_contact_id = $this->decodePrimaryKey($invitation['client_contact_id']); - $new_invitation->save(); - - } - } - } + foreach ($data['invitations'] as $invitation) { + $inv = false; - /* If no invitations have been created, this is our fail safe to maintain state*/ - if ($invoice->invitations->count() == 0) { - $invoice->service()->createInvitations(); - } + if (array_key_exists('key', $invitation)) { + $inv = InvoiceInvitation::whereKey($invitation['key'])->first(); + } - $invoice = $invoice->calc()->getInvoice(); - - $invoice->save(); + if (!$inv) { - $finished_amount = $invoice->amount; + $new_invitation = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); + $new_invitation->fill($invitation); + $new_invitation->invoice_id = $invoice->id; + $new_invitation->client_contact_id = $this->decodePrimaryKey($invitation['client_contact_id']); + $new_invitation->save(); - /**/ - if (($finished_amount != $starting_amount) && ($invoice->status_id != Invoice::STATUS_DRAFT)) { - UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, ($finished_amount - $starting_amount), $invoice->company); - } + } + } + } - $invoice = $invoice->service()->applyNumber()->save(); + /* If no invitations have been created, this is our fail safe to maintain state*/ + if ($invoice->invitations->count() == 0) { + $invoice->service()->createInvitations(); + } - if ($invoice->company->update_products !== false) { - UpdateOrCreateProduct::dispatch($invoice->line_items, $invoice, $invoice->company); - } + $invoice = $invoice->calc()->getInvoice(); - return $invoice->fresh(); - } + $invoice->save(); - /** - * Mark the invoice as sent. - * - * @param \App\Models\Invoice $invoice The invoice - * - * @return Invoice|\App\Models\Invoice|null Return the invoice object - */ - public function markSent(Invoice $invoice) : ?Invoice - { - return $invoice->service()->markSent()->save(); - } + $finished_amount = $invoice->amount; + + /**/ + if (($finished_amount != $starting_amount) && ($invoice->status_id != Invoice::STATUS_DRAFT)) { + UpdateCompanyLedgerWithInvoice::dispatchNow($invoice, ($finished_amount-$starting_amount), $invoice->company); + } + + $invoice = $invoice->service()->applyNumber()->save(); + + if ($invoice->company->update_products !== false) { + UpdateOrCreateProduct::dispatch($invoice->line_items, $invoice, $invoice->company); + } + + return $invoice->fresh(); + } + + /** + * Mark the invoice as sent. + * + * @param \App\Models\Invoice $invoice The invoice + * + * @return Invoice|\App\Models\Invoice|null Return the invoice object + */ + public function markSent(Invoice $invoice):?Invoice { + return $invoice->service()->markSent()->save(); + } } diff --git a/app/Utils/Traits/MakesInvoiceHtml.php b/app/Utils/Traits/MakesInvoiceHtml.php index 7b121c3748eb..83ee61b63a49 100644 --- a/app/Utils/Traits/MakesInvoiceHtml.php +++ b/app/Utils/Traits/MakesInvoiceHtml.php @@ -11,6 +11,7 @@ namespace App\Utils\Traits; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Blade; use Symfony\Component\Debug\Exception\FatalThrowableError; @@ -29,11 +30,20 @@ trait MakesInvoiceHtml * * @return string The invoice string in HTML format */ - public function generateInvoiceHtml($design, $invoice) :string + public function generateInvoiceHtml($design, $invoice, $contact = null) :string { - $variables = array_merge($invoice->makeLabels(), $invoice->makeValues()); + //$variables = array_merge($invoice->makeLabels(), $invoice->makeValues()); + //$design = str_replace(array_keys($variables), array_values($variables), $design); + if(!$contact) + $contact = $invoice->client->primary_contact()->first(); - $design = str_replace(array_keys($variables), array_values($variables), $design); + App::setLocale($contact->preferredLocale()); + + $labels = $invoice->makeLabels(); + $values = $invoice->makeValues($contact); + + $design = str_replace(array_keys($labels), array_values($labels), $design); + $design = str_replace(array_keys($values), array_values($values), $design); $data['invoice'] = $invoice; diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index 6f315c691bfc..e81ddcc59e50 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -119,10 +119,10 @@ trait MakesInvoiceValues 'service', 'product_key', 'unit_cost', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', + // 'custom_value1', + // 'custom_value2', + // 'custom_value3', + // 'custom_value4', 'delivery_note', 'date', 'method', @@ -130,6 +130,49 @@ trait MakesInvoiceValues 'reference', 'amount', 'amount_paid', + 'invoice1', + 'invoice2', + 'invoice3', + 'invoice4', + 'surcharge1', + 'surcharge2', + 'surcharge3', + 'surcharge4', + 'client1', + 'client2', + 'client3', + 'client4', + 'contact1', + 'contact2', + 'contact3', + 'contact4', + 'company1', + 'company2', + 'company3', + 'company4', + ]; + + private static $custom_label_fields = [ + 'invoice1', + 'invoice2', + 'invoice3', + 'invoice4', + 'surcharge1', + 'surcharge2', + 'surcharge3', + 'surcharge4', + 'client1', + 'client2', + 'client3', + 'client4', + 'contact1', + 'contact2', + 'contact3', + 'contact4', + 'company1', + 'company2', + 'company3', + 'company4', ]; /** @@ -150,8 +193,49 @@ trait MakesInvoiceValues $data['$'.$label . '_label'] = ctrans('texts.'.$label); } - if($custom_fields && property_exists($custom_fields,'invoice_text1')) - $data['$invoice_text1'] = $custom_fields->invoice_text1; + if($custom_fields) + { + + foreach($custom_fields as $key => $value) + { + + if(strpos($value, '|') !== false) + { + $value = explode("|", $value); + $value = $value[0]; + } + + $data['$'.$key.'_label'] = $value; + } + + } + + + /* + Don't forget pipe | strings for dropdowns needs to be filtered + */ + + /* + invoice1 + invoice2 + invoice3 + invoice4 + surcharge1 + surcharge2 + surcharge3 + surcharge4 + client1 + client2 + client3 + client4 + contact1 + contact2 + contact3 + contact4 + */ + + $arrKeysLength = array_map('strlen', array_keys($data)); + array_multisort($arrKeysLength, SORT_DESC, $data); return $data; } @@ -179,18 +263,18 @@ trait MakesInvoiceValues $data['$line_tax_labels'] = $this->lineTaxLabels(); $data['$line_tax_values'] = $this->lineTaxValues(); - $data['$date'] = $this->date; + $data['$date'] = $this->date ?: ' '; $data['$invoice.date'] = &$data['$date']; - $data['$due_date'] = $this->due_date; + $data['$due_date'] = $this->due_date ?: ' '; $data['$invoice.due_date'] = &$data['$due_date']; - $data['$number'] = $this->number; + $data['$number'] = $this->number ?: ' '; $data['$invoice.number'] = &$data['$number']; $data['$invoice_number'] = &$data['$number']; - $data['$po_number'] = $this->po_number; + $data['$po_number'] = $this->po_number ?: ' '; $data['$invoice.po_number'] = &$data['$po_number']; - $data['$line_taxes'] = $this->makeLineTaxes(); + $data['$line_taxes'] = $this->makeLineTaxes() ?: ' '; $data['$invoice.line_taxes'] = &$data['$line_taxes']; - $data['$total_taxes'] = $this->makeTotalTaxes(); + $data['$total_taxes'] = $this->makeTotalTaxes() ?: ' '; $data['$invoice.total_taxes'] = &$data['$total_taxes']; // $data['$tax'] = ; // $data['$item'] = ; @@ -199,31 +283,31 @@ trait MakesInvoiceValues // $data['$quantity'] = ; // $data['$line_total'] = ; // $data['$paid_to_date'] = ; - $data['$discount'] = Number::formatMoney($this->calc()->getTotalDiscount(), $this->client); + $data['$discount'] = Number::formatMoney($this->calc()->getTotalDiscount(), $this->client) ?: ' '; $data['$invoice.discount'] = &$data['$discount']; - $data['$subtotal'] = Number::formatMoney($this->calc()->getSubTotal(), $this->client); + $data['$subtotal'] = Number::formatMoney($this->calc()->getSubTotal(), $this->client) ?: ' '; $data['$invoice.subtotal'] = &$data['$subtotal']; - $data['$balance_due'] = Number::formatMoney($this->balance, $this->client); + $data['$balance_due'] = Number::formatMoney($this->balance, $this->client) ?: ' '; $data['$invoice.balance_due'] = &$data['$balance_due']; - $data['$partial_due'] = Number::formatMoney($this->partial, $this->client); + $data['$partial_due'] = Number::formatMoney($this->partial, $this->client) ?: ' '; $data['$invoice.partial_due'] = &$data['$partial_due']; - $data['$total'] = Number::formatMoney($this->calc()->getTotal(), $this->client); + $data['$total'] = Number::formatMoney($this->calc()->getTotal(), $this->client) ?: ' '; $data['$invoice.total'] = &$data['$total']; $data['$amount'] = &$data['$total']; $data['$invoice_total'] = &$data['$total']; $data['$invoice.amount'] = &$data['$total']; - $data['$balance'] = Number::formatMoney($this->calc()->getBalance(), $this->client); + $data['$balance'] = Number::formatMoney($this->calc()->getBalance(), $this->client) ?: ' '; $data['$invoice.balance'] = &$data['$balance']; - $data['$taxes'] = Number::formatMoney($this->calc()->getItemTotalTaxes(), $this->client); + $data['$taxes'] = Number::formatMoney($this->calc()->getItemTotalTaxes(), $this->client) ?: ' '; $data['$invoice.taxes'] = &$data['$taxes']; - $data['$terms'] = $this->terms; + $data['$terms'] = $this->terms ?: ' '; $data['$invoice.terms'] = &$data['$terms']; - $data['$invoice.custom_value1'] = $this->custom_value1; - $data['$invoice.custom_value2'] = $this->custom_value2; - $data['$invoice.custom_value3'] = $this->custom_value3; - $data['$invoice.custom_value4'] = $this->custom_value4; - $data['$invoice.public_notes'] = $this->public_notes; + $data['$invoice1'] = $this->custom_value1 ?: ' '; + $data['$invoice2'] = $this->custom_value2 ?: ' '; + $data['$invoice3'] = $this->custom_value3 ?: ' '; + $data['$invoice4'] = $this->custom_value4 ?: ' '; + $data['$invoice.public_notes'] = $this->public_notes ?: ' '; // $data['$your_invoice'] = ; // $data['$quote'] = ; // $data['$your_quote'] = ; @@ -238,74 +322,74 @@ trait MakesInvoiceValues // $data['$invoice_to'] = ; // $data['$quote_to'] = ; // $data['$details'] = ; - $data['$invoice_no'] = $this->number; + $data['$invoice_no'] = $this->number ?: ' '; $data['$invoice.invoice_no'] = &$data['$invoice_no']; // $data['$quote_no'] = ; // $data['$valid_until'] = ; - $data['$client_name'] = $this->present()->clientName(); + $data['$client1'] = $this->client->custom_value1 ?: ' '; + $data['$client2'] = $this->client->custom_value2 ?: ' '; + $data['$client3'] = $this->client->custom_value3 ?: ' '; + $data['$client4'] = $this->client->custom_value4 ?: ' '; + $data['$client_name'] = $this->present()->clientName() ?: ' '; $data['$client.name'] = &$data['$client_name']; - $data['$client_address'] = $this->present()->address(); - $data['$client.address'] = &$data['$client_address']; - $data['$address1'] = $this->client->address1; - $data['$client.address1'] = &$data['$address1']; - $data['$address2'] = $this->client->address2; + $data['$address1'] = $this->client->address1 ?: ' '; + $data['$address2'] = $this->client->address2 ?: ' '; $data['$client.address2'] = &$data['$address2']; - $data['$id_number'] = $this->client->id_number; + $data['$client.address1'] = &$data['$address1']; + $data['$client.address'] = &$data['$client_address']; + $data['$client_address'] = $this->present()->address() ?: ' '; + $data['$id_number'] = $this->client->id_number ?: ' '; $data['$client.id_number'] = &$data['$id_number']; - $data['$vat_number'] = $this->client->vat_number; + $data['$vat_number'] = $this->client->vat_number ?: ' '; $data['$client.vat_number'] = &$data['$vat_number']; - $data['$website'] = $this->client->present()->website(); + $data['$website'] = $this->client->present()->website() ?: ' '; $data['$client.website'] = &$data['$website']; - $data['$phone'] = $this->client->present()->phone(); + $data['$phone'] = $this->client->present()->phone() ?: ' '; $data['$client.phone'] = &$data['$phone']; - $data['$city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false); + $data['$city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false) ?: ' '; $data['$client.city_state_postal'] = &$data['$city_state_postal']; - $data['$postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true); + $data['$postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true) ?: ' '; $data['$client.postal_city_state'] = &$data['$postal_city_state']; - $data['$country'] = isset($this->client->country->name) ?: 'No Country Set'; + $data['$country'] = isset($this->client->country->name) ? $this->client->country->name : 'No Country Set'; $data['$client.country'] = &$data['$country']; - $data['$email'] = isset($this->client->primary_contact()->first()->email) ?: 'no contact email on record'; + $data['$email'] = isset($this->client->primary_contact()->first()->email) ? $this->client->primary_contact()->first()->email : 'no contact email on record'; $data['$client.email'] = &$data['$email']; - $data['$client.custom_value1'] = $this->client->custom_value1; - $data['$client.custom_value2'] = $this->client->custom_value2; - $data['$client.custom_value3'] = $this->client->custom_value3; - $data['$client.custom_value4'] = $this->client->custom_value4; if(!$contact) $contact = $this->client->primary_contact()->first(); $data['$contact_name'] = isset($contact) ? $contact->present()->name() : 'no contact name on record'; $data['$contact.name'] = &$data['$contact_name']; - $data['$contact.custom_value1'] = isset($contact) ? $contact->custom_value1 : ''; - $data['$contact.custom_value2'] = isset($contact) ? $contact->custom_value2 : ''; - $data['$contact.custom_value3'] = isset($contact) ? $contact->custom_value3 : ''; - $data['$contact.custom_value4'] = isset($contact) ? $contact->custom_value4 : ''; + $data['$contact1'] = isset($contact) ? $contact->custom_value1 : ' '; + $data['$contact2'] = isset($contact) ? $contact->custom_value2 : ' '; + $data['$contact3'] = isset($contact) ? $contact->custom_value3 : ' '; + $data['$contact4'] = isset($contact) ? $contact->custom_value4 : ' '; - $data['$company.city_state_postal'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, false); - $data['$company.postal_city_state'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, true); - $data['$company.name'] = $this->company->present()->name(); + $data['$company.city_state_postal'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, false) ?: ' '; + $data['$company.postal_city_state'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, true) ?: ' '; + $data['$company.name'] = $this->company->present()->name() ?: ' '; $data['$company.company_name'] = &$data['$company.name']; - $data['$company.address1'] = $settings->address1; - $data['$company.address2'] = $settings->address2; - $data['$company.city'] = $settings->city; - $data['$company.state'] = $settings->state; - $data['$company.postal_code'] = $settings->postal_code; - $data['$company.country'] = Country::find($settings->country_id)->first()->name; - $data['$company.phone'] = $settings->phone; - $data['$company.email'] = $settings->email; - $data['$company.vat_number'] = $settings->vat_number; - $data['$company.id_number'] = $settings->id_number; - $data['$company.website'] = $settings->website; - $data['$company.address'] = $this->company->present()->address($settings); + $data['$company.address1'] = $settings->address1 ?: ' '; + $data['$company.address2'] = $settings->address2 ?: ' '; + $data['$company.city'] = $settings->city ?: ' '; + $data['$company.state'] = $settings->state ?: ' '; + $data['$company.postal_code'] = $settings->postal_code ?: ' '; + $data['$company.country'] = Country::find($settings->country_id)->first()->name ?: ' '; + $data['$company.phone'] = $settings->phone ?: ' '; + $data['$company.email'] = $settings->email ?: ' '; + $data['$company.vat_number'] = $settings->vat_number ?: ' '; + $data['$company.id_number'] = $settings->id_number ?: ' '; + $data['$company.website'] = $settings->website ?: ' '; + $data['$company.address'] = $this->company->present()->address($settings) ?: ' '; $logo = $this->company->present()->logo($settings); - $data['$company.logo'] = "logo"; + $data['$company.logo'] = "logo" ?: ' '; $data['$company_logo'] = &$data['$company.logo']; - $data['$company.custom_value1'] = $this->company->custom_value1; - $data['$company.custom_value2'] = $this->company->custom_value2; - $data['$company.custom_value3'] = $this->company->custom_value3; - $data['$company.custom_value4'] = $this->company->custom_value4; + $data['$company1'] = $settings->custom_value1 ?: ' '; + $data['$company2'] = $settings->custom_value2 ?: ' '; + $data['$company3'] = $settings->custom_value3 ?: ' '; + $data['$company4'] = $settings->custom_value4 ?: ' '; //$data['$blank'] = ; //$data['$surcharge'] = ; /* @@ -340,6 +424,12 @@ trait MakesInvoiceValues $data['$amount'] = ; $data['$amount_paid'] =; */ + + $arrKeysLength = array_map('strlen', array_keys($data)); + array_multisort($arrKeysLength, SORT_DESC, $data); + // \Log::error('woop'); + //\Log::error(print_r($data,1)); + return $data; } @@ -499,6 +589,26 @@ trait MakesInvoiceValues $item->discount = $item->discount . '%'; } } + else + $item->discount = ''; + + if(isset($item->tax_rate1) && $item->tax_rate1 > 0) + $item->tax_rate1 = $item->tax_rate1."%"; + + if(isset($item->tax_rate2) && $item->tax_rate2 > 0) + $item->tax_rate2 = $item->tax_rate2."%"; + + if(isset($item->tax_rate2) && $item->tax_rate2 > 0) + $item->tax_rate2 = $item->tax_rate2."%"; + + if(isset($item->tax_rate1) && $item->tax_rate1 == 0) + $item->tax_rate1 = ''; + + if(isset($item->tax_rate2) && $item->tax_rate2 == 0) + $item->tax_rate2 = ''; + + if(isset($item->tax_rate2) && $item->tax_rate2 == 0) + $item->tax_rate2 = ''; } diff --git a/config/self-update.php b/config/self-update.php index 05571ac6a20f..04d04c54b62f 100644 --- a/config/self-update.php +++ b/config/self-update.php @@ -2,124 +2,126 @@ return [ - /* - |-------------------------------------------------------------------------- - | Default source repository type - |-------------------------------------------------------------------------- - | - | The default source repository type you want to pull your updates from. - | - */ + /* + |-------------------------------------------------------------------------- + | Default source repository type + |-------------------------------------------------------------------------- + | + | The default source repository type you want to pull your updates from. + | + */ - 'default' => env('SELF_UPDATER_SOURCE', 'github'), + 'default' => env('SELF_UPDATER_SOURCE', 'github'), - /* - |-------------------------------------------------------------------------- - | Version installed - |-------------------------------------------------------------------------- - | - | Set this to the version of your software installed on your system. - | - */ + /* + |-------------------------------------------------------------------------- + | Version installed + |-------------------------------------------------------------------------- + | + | Set this to the version of your software installed on your system. + | + */ - 'version_installed' => env('SELF_UPDATER_VERSION_INSTALLED', ''), + 'version_installed' => env('SELF_UPDATER_VERSION_INSTALLED', ''), - /* - |-------------------------------------------------------------------------- - | Repository types - |-------------------------------------------------------------------------- - | - | A repository can be of different types, which can be specified here. - | Current options: - | - github - | - http - | - */ + /* + |-------------------------------------------------------------------------- + | Repository types + |-------------------------------------------------------------------------- + | + | A repository can be of different types, which can be specified here. + | Current options: + | - github + | - http + | + */ - 'repository_types' => [ - 'github' => [ - 'type' => 'github', - 'repository_vendor' => env('SELF_UPDATER_REPO_VENDOR', ''), - 'repository_name' => env('SELF_UPDATER_REPO_NAME', ''), - 'repository_url' => '', - 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), - 'private_access_token' => env('SELF_UPDATER_GITHUB_PRIVATE_ACCESS_TOKEN', ''), - ], - 'http' => [ - 'type' => 'http', - 'repository_url' => env('SELF_UPDATER_REPO_URL', ''), - 'pkg_filename_format' => env('SELF_UPDATER_PKG_FILENAME_FORMAT', 'v_VERSION_'), - 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), - 'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''), - ], - ], + 'repository_types' => [ + 'github' => [ + 'type' => 'github', + 'repository_vendor' => env('SELF_UPDATER_REPO_VENDOR', ''), + 'repository_name' => env('SELF_UPDATER_REPO_NAME', ''), + 'repository_url' => '', + 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), + 'private_access_token' => env('SELF_UPDATER_GITHUB_PRIVATE_ACCESS_TOKEN', ''), + 'use_branch' => env('SELF_UPDATER_BRANCH_NAME', 'v2'), - /* - |-------------------------------------------------------------------------- - | Exclude folders from update - |-------------------------------------------------------------------------- - | - | Specifiy folders which should not be updated and will be skipped during the - | update process. - | - | Here's already a list of good examples to skip. You may want to keep those. - | - */ + ], + 'http' => [ + 'type' => 'http', + 'repository_url' => env('SELF_UPDATER_REPO_URL', ''), + 'pkg_filename_format' => env('SELF_UPDATER_PKG_FILENAME_FORMAT', 'v_VERSION_'), + 'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'), + 'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''), + ], + ], - 'exclude_folders' => [ - 'node_modules', - 'bootstrap/cache', - 'bower', - 'storage/app', - 'storage/framework', - 'storage/logs', - 'storage/self-update', - 'vendor', - ], + /* + |-------------------------------------------------------------------------- + | Exclude folders from update + |-------------------------------------------------------------------------- + | + | Specifiy folders which should not be updated and will be skipped during the + | update process. + | + | Here's already a list of good examples to skip. You may want to keep those. + | + */ - /* - |-------------------------------------------------------------------------- - | Event Logging - |-------------------------------------------------------------------------- - | - | Configure if fired events should be logged - | - */ + 'exclude_folders' => [ + 'node_modules', + 'bootstrap/cache', + 'bower', + 'storage/app', + 'storage/framework', + 'storage/logs', + 'storage/self-update', + 'vendor', + ], - 'log_events' => env('SELF_UPDATER_LOG_EVENTS', false), + /* + |-------------------------------------------------------------------------- + | Event Logging + |-------------------------------------------------------------------------- + | + | Configure if fired events should be logged + | + */ - /* - |-------------------------------------------------------------------------- - | Mail To Settings - |-------------------------------------------------------------------------- - | - | Configure if fired events should be logged - | - */ + 'log_events' => env('SELF_UPDATER_LOG_EVENTS', false), - 'mail_to' => [ - 'address' => env('SELF_UPDATER_MAILTO_ADDRESS', ''), - 'name' => env('SELF_UPDATER_MAILTO_NAME', ''), - 'subject_update_available' => env('SELF_UPDATER_MAILTO_UPDATE_AVAILABLE_SUBJECT', 'Update available'), - 'subject_update_succeeded' => env('SELF_UPDATER_MAILTO_UPDATE_SUCCEEDED_SUBJECT', 'Update succeeded'), - ], + /* + |-------------------------------------------------------------------------- + | Mail To Settings + |-------------------------------------------------------------------------- + | + | Configure if fired events should be logged + | + */ - /* - |--------------------------------------------------------------------------- - | Register custom artisan commands - |--------------------------------------------------------------------------- - */ + 'mail_to' => [ + 'address' => env('SELF_UPDATER_MAILTO_ADDRESS', ''), + 'name' => env('SELF_UPDATER_MAILTO_NAME', ''), + 'subject_update_available' => env('SELF_UPDATER_MAILTO_UPDATE_AVAILABLE_SUBJECT', 'Update available'), + 'subject_update_succeeded' => env('SELF_UPDATER_MAILTO_UPDATE_SUCCEEDED_SUBJECT', 'Update succeeded'), + ], - 'artisan_commands' => [ - 'pre_update' => [ - //'command:signature' => [ - // 'class' => Command class - // 'params' => [] - //] - ], - 'post_update' => [ + /* + |--------------------------------------------------------------------------- + | Register custom artisan commands + |--------------------------------------------------------------------------- + */ - ], - ], + 'artisan_commands' => [ + 'pre_update' => [ + //'command:signature' => [ + // 'class' => Command class + // 'params' => [] + //] + ], + 'post_update' => [ + + ], + ], ]; diff --git a/database/factories/CompanyFactory.php b/database/factories/CompanyFactory.php index f5ddfb9e3a80..f96054f399f3 100644 --- a/database/factories/CompanyFactory.php +++ b/database/factories/CompanyFactory.php @@ -10,7 +10,7 @@ $factory->define(App\Models\Company::class, function (Faker $faker) { 'ip' => $faker->ipv4, 'db' => config('database.default'), 'settings' => CompanySettings::defaults(), - 'custom_fields' => (object) ['custom1' => '1', 'custom2' => '2', 'custom3'=>'3'], + 'custom_fields' => (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'], // 'address1' => $faker->secondaryAddress, // 'address2' => $faker->address, diff --git a/routes/api.php b/routes/api.php index f9eab0ad1423..f2e9cd74481c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,7 +1,5 @@ get('/user', function (Request $request) { - return $request->user(); +return $request->user(); }); -*/ + */ -Route::group(['middleware' => ['api_secret_check']], function () { +Route::group(['middleware' => ['api_secret_check']], + function () { - Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit'); - Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin'); + Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit'); + Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin'); -}); + }); Route::group(['api_secret_check', 'email_db'], function () { - Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit'); - Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset'); + Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit'); + Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset'); -}); + }); Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () { - Route::resource('activities', 'ActivityController'); // name = (clients. index / create / show / update / destroy / edit + Route::resource('activities', 'ActivityController');// name = (clients. index / create / show / update / destroy / edit - Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit + Route::resource('clients', 'ClientController');// name = (clients. index / create / show / update / destroy / edit - Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk'); + Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk'); - Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit - - Route::get('invoices/{invoice}/{action}', 'InvoiceController@action')->name('invoices.action'); - - Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk'); + Route::resource('invoices', 'InvoiceController');// name = (invoices. index / create / show / update / destroy / edit - Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit + Route::get('invoices/{invoice}/{action}', 'InvoiceController@action')->name('invoices.action'); - Route::get('credits/{credit}/{action}', 'CreditController@action')->name('credits.action'); + Route::get('invoice/{invitation_key}/download', 'InvoiceController@downloadPdf')->name('invoices.downloadPdf'); - Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk'); + Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk'); - Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit + Route::resource('credits', 'CreditController');// name = (credits. index / create / show / update / destroy / edit - Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); + Route::get('credits/{credit}/{action}', 'CreditController@action')->name('credits.action'); - Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit + Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk'); - Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk'); + Route::resource('products', 'ProductController');// name = (products. index / create / show / update / destroy / edit - Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit + Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); - Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk'); + Route::resource('quotes', 'QuoteController');// name = (quotes. index / create / show / update / destroy / edit - Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit + Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk'); - Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk'); + Route::resource('recurring_invoices', 'RecurringInvoiceController');// name = (recurring_invoices. index / create / show / update / destroy / edit - Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit + Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk'); - Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); + Route::resource('recurring_quotes', 'RecurringQuoteController');// name = (recurring_invoices. index / create / show / update / destroy / edit - Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit + Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk'); - Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk'); + Route::resource('expenses', 'ExpenseController');// name = (expenses. index / create / show / update / destroy / edit - Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit + Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); - Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit + Route::resource('vendors', 'VendorController');// name = (vendors. index / create / show / update / destroy / edit - Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund'); + Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk'); - Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); + Route::resource('client_statement', 'ClientStatementController@statement');// name = (client_statement. index / create / show / update / destroy / edit - Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start'); + Route::resource('payments', 'PaymentController');// name = (payments. index / create / show / update / destroy / edit -// Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit - Route::get('users', 'UserController@index'); - Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); - Route::post('users', 'UserController@store')->middleware('password_protected'); - Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected'); - Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected'); + Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund'); + + Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); + + Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start'); + + // Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit + Route::get('users', 'UserController@index'); + Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); + Route::post('users', 'UserController@store')->middleware('password_protected'); + Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected'); + Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected'); + + Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected'); + + Route::post('migration/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected'); + Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected'); + Route::post('migration/start', 'MigrationController@startMigration')->middleware('password_protected'); + + Route::resource('companies', 'CompanyController');// name = (companies. index / create / show / update / destroy / edit + + Route::resource('company_gateways', 'CompanyGatewayController'); + + Route::resource('group_settings', 'GroupSettingController'); + + Route::resource('tax_rates', 'TaxRateController');// name = (tasks. index / create / show / update / destroy / edit + + Route::post('refresh', 'Auth\LoginController@refresh'); + + Route::post('templates', 'TemplateController@show')->name('templates.show'); + + Route::post('self-update', 'SelfUpdateController@update'); + + /* + Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit + + Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); - Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected'); + Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit - Route::post('migration/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected'); - Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected'); - Route::post('migration/start', 'MigrationController@startMigration')->middleware('password_protected'); - - Route::resource('companies', 'CompanyController'); // name = (companies. index / create / show / update / destroy / edit - - Route::resource('company_gateways', 'CompanyGatewayController'); - - Route::resource('group_settings', 'GroupSettingController'); - - Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit - - Route::post('refresh', 'Auth\LoginController@refresh'); - - Route::post('templates', 'TemplateController@show')->name('templates.show'); - - /* - Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit - - Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); - - - Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit - - Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk'); + Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk'); - Route::get('settings', 'SettingsController@index')->name('user.settings'); - */ - Route::post('support/messages/send', 'Support\Messages\SendingController'); -}); + Route::get('settings', 'SettingsController@index')->name('user.settings'); + */ + Route::post('support/messages/send', 'Support\Messages\SendingController'); + }); Route::fallback('BaseController@notFound'); diff --git a/tests/Feature/PaymentTest.php b/tests/Feature/PaymentTest.php index f68e9362a78a..65b99e877727 100644 --- a/tests/Feature/PaymentTest.php +++ b/tests/Feature/PaymentTest.php @@ -281,6 +281,14 @@ class PaymentTest extends TestCase $client = ClientFactory::create($this->company->id, $this->user->id); $client->save(); + factory(\App\Models\ClientContact::class)->create([ + 'user_id' => $this->user->id, + 'client_id' => $client->id, + 'company_id' =>$this->company->id, + 'is_primary' => true, + ]); + + $this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id $this->invoice->client_id = $client->id; diff --git a/tests/Integration/InvoiceDesignTest.php b/tests/Integration/InvoiceDesignTest.php index 9c1a83c3e880..41d9a4e2cec3 100644 --- a/tests/Integration/InvoiceDesignTest.php +++ b/tests/Integration/InvoiceDesignTest.php @@ -39,10 +39,14 @@ class InvoiceDesignTest extends TestCase 'postal_city_state', 'country', 'email', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', + 'client1', + 'client2', + 'client3', + 'client4', + 'contact1', + 'contact2', + 'contact3', + 'contact4', ], 'company_details' => [ 'company_name', @@ -51,10 +55,10 @@ class InvoiceDesignTest extends TestCase 'website', 'email', 'phone', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', + 'company1', + 'company2', + 'company3', + 'company4', ], 'company_address' => [ 'address1', @@ -62,10 +66,10 @@ class InvoiceDesignTest extends TestCase 'city_state_postal', 'postal_city_state', 'country', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', + 'company1', + 'company2', + 'company3', + 'company4', ], 'invoice_details' => [ 'invoice_number', @@ -75,10 +79,14 @@ class InvoiceDesignTest extends TestCase 'balance_due', 'invoice_total', 'partial_due', - 'custom_value1', - 'custom_value2', - 'custom_value3', - 'custom_value4', + 'invoice1', + 'invoice2', + 'invoice3', + 'invoice4', + 'surcharge1', + 'surcharge2', + 'surcharge3', + 'surcharge4', ], 'table_columns' => [ 'product_key', @@ -99,7 +107,7 @@ class InvoiceDesignTest extends TestCase //\Log::error($html); - CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company); + CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company, $this->invoice->client->primary_contact()->first()); }