From c35f9fbe04c237dc1288378bd87049b2870a7d0b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 3 May 2019 08:29:04 +1000 Subject: [PATCH] recurring invoices --- app/DataMapper/ClientSettings.php | 3 - app/Factory/RecurringInvoiceFactory.php | 51 +++++ .../RecurringInvoiceController.php | 200 ++++++++++++++--- .../ActionRecurringInvoiceRequest.php | 21 ++ .../CreateRecurringInvoiceRequest.php | 21 ++ .../DestroyRecurringInvoiceRequest.php | 21 ++ .../EditRecurringInvoiceRequest.php | 40 ++++ .../ShowRecurringInvoiceRequest.php | 21 ++ .../StoreRecurringInvoiceRequest.php | 41 ++++ .../UpdateRecurringInvoiceRequest.php | 32 +++ app/Models/RecurringInvoice.php | 37 ++++ app/Policies/RecurringInvoicePolicy.php | 25 +++ app/Providers/AuthServiceProvider.php | 3 + .../RecurringInvoiceRepository.php | 38 ++++ .../RecurringInvoiceTransformer.php | 161 ++++++++++++++ .../factories/RecurringInvoiceFactory.php | 32 +++ .../2014_10_13_000000_create_users_table.php | 4 +- tests/Feature/RecurringInvoiceTest.php | 203 ++++++++++++++++++ 18 files changed, 925 insertions(+), 29 deletions(-) create mode 100644 app/Factory/RecurringInvoiceFactory.php create mode 100644 app/Http/Requests/RecurringInvoice/ActionRecurringInvoiceRequest.php create mode 100644 app/Http/Requests/RecurringInvoice/CreateRecurringInvoiceRequest.php create mode 100644 app/Http/Requests/RecurringInvoice/DestroyRecurringInvoiceRequest.php create mode 100644 app/Http/Requests/RecurringInvoice/EditRecurringInvoiceRequest.php create mode 100644 app/Http/Requests/RecurringInvoice/ShowRecurringInvoiceRequest.php create mode 100644 app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php create mode 100644 app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php create mode 100644 app/Policies/RecurringInvoicePolicy.php create mode 100644 app/Repositories/RecurringInvoiceRepository.php create mode 100644 app/Transformers/RecurringInvoiceTransformer.php create mode 100644 database/factories/RecurringInvoiceFactory.php create mode 100644 tests/Feature/RecurringInvoiceTest.php diff --git a/app/DataMapper/ClientSettings.php b/app/DataMapper/ClientSettings.php index 128a25b8c3ff..e5845a0c7ce0 100644 --- a/app/DataMapper/ClientSettings.php +++ b/app/DataMapper/ClientSettings.php @@ -59,9 +59,6 @@ class ClientSettings extends BaseSettings public $quote_number_prefix; public $quote_number_pattern; public $quote_number_counter; - - //public $client_number_prefix; - //public $client_number_pattern; public $credit_number_prefix; public $credit_number_pattern; diff --git a/app/Factory/RecurringInvoiceFactory.php b/app/Factory/RecurringInvoiceFactory.php new file mode 100644 index 000000000000..b656eda9af03 --- /dev/null +++ b/app/Factory/RecurringInvoiceFactory.php @@ -0,0 +1,51 @@ +status_id = RecurringInvoice::STATUS_PENDING; + $invoice->discount = 0; + $invoice->is_amount_discount = true; + $invoice->po_number = ''; + $invoice->footer = ''; + $invoice->terms = ''; + $invoice->public_notes = ''; + $invoice->private_notes = ''; + $invoice->invoice_date = null; + $invoice->due_date = null; + $invoice->partial_due_date = null; + $invoice->is_deleted = false; + $invoice->line_items = json_encode([]); + $invoice->settings = ClientSettings::buildClientSettings(new CompanySettings(CompanySettings::defaults()), new ClientSettings(ClientSettings::defaults())); //todo need to embed the settings here + $invoice->backup = json_encode([]); + $invoice->tax_name1 = ''; + $invoice->tax_rate1 = 0; + $invoice->tax_name2 = ''; + $invoice->tax_rate2 = 0; + $invoice->custom_value1 = 0; + $invoice->custom_value2 = 0; + $invoice->custom_value3 = 0; + $invoice->custom_value4 = 0; + $invoice->amount = 0; + $invoice->balance = 0; + $invoice->partial = 0; + $invoice->user_id = $user_id; + $invoice->company_id = $company_id; + $invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY; + $invoice->start_date = null; + $invoice->last_sent_date = null; + $invoice->next_send_date = null; + $invoice->remaining_cycles = 0; + + return $invoice; + } + +} diff --git a/app/Http/Controllers/RecurringInvoiceController.php b/app/Http/Controllers/RecurringInvoiceController.php index fe680cd059c4..8fc7c273c525 100644 --- a/app/Http/Controllers/RecurringInvoiceController.php +++ b/app/Http/Controllers/RecurringInvoiceController.php @@ -2,83 +2,235 @@ namespace App\Http\Controllers; +use App\Factory\CloneRecurringInvoiceFactory; +use App\Factory\CloneRecurringInvoiceToQuoteFactory; +use App\Factory\RecurringInvoiceFactory; +use App\Filters\RecurringInvoiceFilters; +use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\EditRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest; +use App\Jobs\Entity\ActionEntity; +use App\Models\RecurringInvoice; +use App\Repositories\BaseRepository; +use App\Repositories\RecurringInvoiceRepository; +use App\Transformers\RecurringInvoiceTransformer; +use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; -class RecurringInvoiceController extends Controller +/** + * Class RecurringInvoiceController + * @package App\Http\Controllers\RecurringInvoiceController + */ + +class RecurringInvoiceController extends BaseController { + + use MakesHash; + + protected $entity_type = RecurringInvoice::class; + + protected $entity_transformer = RecurringInvoiceTransformer::class; + /** - * Display a listing of the resource. + * @var RecurringInvoiceRepository + */ + protected $recurring_invoice_repo; + + protected $base_repo; + + /** + * RecurringInvoiceController constructor. + * + * @param \App\Repositories\RecurringInvoiceRepository $recurring_invoice_repo The RecurringInvoice repo + */ + public function __construct(RecurringInvoiceRepository $recurring_invoice_repo) + { + + parent::__construct(); + + $this->recurring_invoice_repo = $recurring_invoice_repo; + + } + + /** + * Show the list of recurring_invoices + * + * @param \App\Filters\RecurringInvoiceFilters $filters The filters * * @return \Illuminate\Http\Response */ - public function index() + public function index(RecurringInvoiceFilters $filters) { - // + + $recurring_invoices = RecurringInvoice::filter($filters); + + return $this->listResponse($recurring_invoices); + } /** * Show the form for creating a new resource. * + * @param \App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest $request The request + * * @return \Illuminate\Http\Response */ - public function create() + public function create(CreateRecurringInvoiceRequest $request) { - // + + $recurring_invoice = RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id); + + return $this->itemResponse($recurring_invoice); + } + /** * Store a newly created resource in storage. * - * @param \Illuminate\Http\Request $request + * @param \App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest $request The request + * * @return \Illuminate\Http\Response */ - public function store(Request $request) + public function store(StoreRecurringInvoiceRequest $request) { - // + + $recurring_invoice = $this->RecurringInvoice_repo->save($request, RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id)); + + return $this->itemResponse($recurring_invoice); + } /** * Display the specified resource. * - * @param int $id + * @param \App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest $request The request + * @param \App\Models\RecurringInvoice $recurring_invoice The RecurringInvoice + * * @return \Illuminate\Http\Response */ - public function show($id) + public function show(ShowRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice) { - // + + return $this->itemResponse($recurring_invoice); + } /** * Show the form for editing the specified resource. * - * @param int $id + * @param \App\Http\Requests\RecurringInvoice\EditRecurringInvoiceRequest $request The request + * @param \App\Models\RecurringInvoice $recurring_invoice The RecurringInvoice + * * @return \Illuminate\Http\Response */ - public function edit($id) + public function edit(EditRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice) { - // - } + return $this->itemResponse($recurring_invoice); + + } + /** * Update the specified resource in storage. * - * @param \Illuminate\Http\Request $request - * @param int $id + * @param \App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest $request The request + * @param \App\Models\RecurringInvoice $recurring_invoice The RecurringInvoice + * * @return \Illuminate\Http\Response */ - public function update(Request $request, $id) + public function update(UpdateRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice) { - // + + $recurring_invoice = $this->RecurringInvoice_repo->save(request(), $recurring_invoice); + + return $this->itemResponse($recurring_invoice); + } /** * Remove the specified resource from storage. * - * @param int $id - * @return \Illuminate\Http\Response + * @param \App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest $request + * @param \App\Models\RecurringInvoice $recurring_invoice + * + * @return \Illuminate\Http\Response */ - public function destroy($id) + public function destroy(DestroyRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice) { - // + + $recurring_invoice->delete(); + + return response()->json([], 200); + } + + /** + * Perform bulk actions on the list view + * + * @return Collection + */ + public function bulk() + { + + $action = request()->input('action'); + + $ids = request()->input('ids'); + + $recurring_invoices = RecurringInvoice::withTrashed()->find($ids); + + $recurring_invoices->each(function ($recurring_invoice, $key) use($action){ + + if(auth()->user()->can('edit', $recurring_invoice)) + $this->RecurringInvoice_repo->{$action}($recurring_invoice); + + }); + + //todo need to return the updated dataset + return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $ids)); + + } + + public function action(ActionRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice, $action) + { + + switch ($action) { + case 'clone_to_RecurringInvoice': + // $recurring_invoice = CloneRecurringInvoiceFactory::create($recurring_invoice, auth()->user()->id); + // return $this->itemResponse($recurring_invoice); + break; + case 'clone_to_quote': + // $quote = CloneRecurringInvoiceToQuoteFactory::create($recurring_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': + # code... + break; + case 'archive': + # code... + break; + case 'delete': + # code... + break; + case 'email': + //dispatch email to queue + break; + + default: + # code... + break; + } + } + } diff --git a/app/Http/Requests/RecurringInvoice/ActionRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/ActionRecurringInvoiceRequest.php new file mode 100644 index 000000000000..26ddad27aba2 --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/ActionRecurringInvoiceRequest.php @@ -0,0 +1,21 @@ +user()->can('edit', $this->recurring_invoice); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/RecurringInvoice/CreateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/CreateRecurringInvoiceRequest.php new file mode 100644 index 000000000000..2344cbad2051 --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/CreateRecurringInvoiceRequest.php @@ -0,0 +1,21 @@ +user()->can('create', RecurringInvoice::class); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/RecurringInvoice/DestroyRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/DestroyRecurringInvoiceRequest.php new file mode 100644 index 000000000000..3e21d60e502b --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/DestroyRecurringInvoiceRequest.php @@ -0,0 +1,21 @@ +user()->can('edit', $this->recurring_invoice); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/RecurringInvoice/EditRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/EditRecurringInvoiceRequest.php new file mode 100644 index 000000000000..5ed0432bcda4 --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/EditRecurringInvoiceRequest.php @@ -0,0 +1,40 @@ +user()->can('edit', $this->recurring_invoice); + } + + public function rules() + { + $rules = []; + + return $rules; + } + + + public function sanitize() + { + $input = $this->all(); + + //$input['id'] = $this->encodePrimaryKey($input['id']); + + //$this->replace($input); + + return $this->all(); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/RecurringInvoice/ShowRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/ShowRecurringInvoiceRequest.php new file mode 100644 index 000000000000..4d3c8138ec8d --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/ShowRecurringInvoiceRequest.php @@ -0,0 +1,21 @@ +user()->can('view', $this->recurring_invoice); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php new file mode 100644 index 000000000000..7be5d6bd89d4 --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php @@ -0,0 +1,41 @@ +user()->can('create', RecurringInvoice::class); + } + + public function rules() + { + return [ + 'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx', + ]; + } + + + public function sanitize() + { + //do post processing of RecurringInvoice request here, ie. RecurringInvoice_items + } + + public function messages() + { + + } + + +} + diff --git a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php new file mode 100644 index 000000000000..ec11b37cb677 --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php @@ -0,0 +1,32 @@ +user()->can('edit', $this->recurring_invoice); + + } + + + public function rules() + { + return [ + 'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx', + ]; + } + +} \ No newline at end of file diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index b84ce24c719b..443c1437fcfa 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -7,12 +7,39 @@ use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +/** + * Class for Recurring Invoices. + */ class RecurringInvoice extends BaseModel { use MakesHash; use SoftDeletes; use Filterable; + /** + * Invoice Statuses + */ + const STATUS_PENDING = 1; + const STATUS_ACTIVE = 2; + const STATUS_COMPLETED = 3; + const STATUS_CANCELLED = 4; + + /** + * Recurring intervals + */ + const FREQUENCY_WEEKLY = 1; + const FREQUENCY_TWO_WEEKS = 2; + const FREQUENCY_FOUR_WEEKS = 3; + const FREQUENCY_MONTHLY = 4; + const FREQUENCY_TWO_MONTHS = 5; + const FREQUENCY_THREE_MONTHS = 6; + const FREQUENCY_FOUR_MONTHS = 7; + const FREQUENCY_SIX_MONTHS = 8; + const FREQUENCY_ANNUALLY = 9; + const FREQUENCY_TWO_YEARS = 10; + + const RECURS_INDEFINITELY = 1; + protected $guarded = [ 'id', ]; @@ -21,11 +48,21 @@ class RecurringInvoice extends BaseModel 'settings' => 'object' ]; + protected $with = [ + 'client', + 'company', + ]; + public function company() { return $this->belongsTo(Company::class); } + public function client() + { + return $this->belongsTo(Client::class); + } + public function user() { return $this->belongsTo(User::class); diff --git a/app/Policies/RecurringInvoicePolicy.php b/app/Policies/RecurringInvoicePolicy.php new file mode 100644 index 000000000000..ab255cce577e --- /dev/null +++ b/app/Policies/RecurringInvoicePolicy.php @@ -0,0 +1,25 @@ +isAdmin() || $user->hasPermission('create_recurring_invoice'); + } + +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index f48b7ba08831..91065a771b59 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -6,11 +6,13 @@ use App\Models\Client; use App\Models\Invoice; use App\Models\Product; use App\Models\Quote; +use App\Models\RecurringInvoice; use App\Models\User; use App\Policies\ClientPolicy; use App\Policies\InvoicePolicy; use App\Policies\ProductPolicy; use App\Policies\QuotePolicy; +use App\Policies\RecurringInvoicePolicy; use Auth; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; @@ -26,6 +28,7 @@ class AuthServiceProvider extends ServiceProvider Client::class => ClientPolicy::class, Product::class => ProductPolicy::class, Invoice::class => InvoicePolicy::class, + RecurringInvoice::class => RecurringInvoicePolicy::class, Quote::class => QuotePolicy::class, User::class => UserPolicy::class, ]; diff --git a/app/Repositories/RecurringInvoiceRepository.php b/app/Repositories/RecurringInvoiceRepository.php new file mode 100644 index 000000000000..b103ce1f7036 --- /dev/null +++ b/app/Repositories/RecurringInvoiceRepository.php @@ -0,0 +1,38 @@ +fill($request->input()); + + $invoice->save(); + + + $invoice_calc = new InvoiceCalc($invoice, $invoice->settings); + + $invoice = $invoice_calc->build()->getInvoice(); + + //fire events here that cascading from the saving of an invoice + //ie. client balance update... + + return $invoice; + } + +} \ No newline at end of file diff --git a/app/Transformers/RecurringInvoiceTransformer.php b/app/Transformers/RecurringInvoiceTransformer.php new file mode 100644 index 000000000000..5795e8c10504 --- /dev/null +++ b/app/Transformers/RecurringInvoiceTransformer.php @@ -0,0 +1,161 @@ +serializer); + + return $this->includeCollection($invoice->invoice_items, $transformer, ENTITY_INVOICE_ITEM); + } + + public function includeInvitations(Invoice $invoice) + { + $transformer = new InvitationTransformer($this->account, $this->serializer); + + return $this->includeCollection($invoice->invitations, $transformer, ENTITY_INVITATION); + } + + public function includePayments(Invoice $invoice) + { + $transformer = new PaymentTransformer($this->account, $this->serializer, $invoice); + + return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT); + } + + public function includeClient(Invoice $invoice) + { + $transformer = new ClientTransformer($this->account, $this->serializer); + + return $this->includeItem($invoice->client, $transformer, ENTITY_CLIENT); + } + + public function includeExpenses(Invoice $invoice) + { + $transformer = new ExpenseTransformer($this->account, $this->serializer); + + return $this->includeCollection($invoice->expenses, $transformer, ENTITY_EXPENSE); + } + + public function includeDocuments(Invoice $invoice) + { + $transformer = new DocumentTransformer($this->account, $this->serializer); + + $invoice->documents->each(function ($document) use ($invoice) { + $document->setRelation('invoice', $invoice); + }); + + return $this->includeCollection($invoice->documents, $transformer, ENTITY_DOCUMENT); + } +*/ + public function transform(RecurringInvoice $invoice) + { + return [ + 'id' => $this->encodePrimaryKey($invoice->id), + 'amount' => (float) $invoice->amount, + 'balance' => (float) $invoice->balance, + 'client_id' => (int) $invoice->client_id, + 'status_id' => (int) ($invoice->status_id ?: 1), + 'updated_at' => $invoice->updated_at, + 'archived_at' => $invoice->deleted_at, + 'discount' => (float) $invoice->discount, + 'po_number' => $invoice->po_number, + 'invoice_date' => $invoice->invoice_date ?: '', + 'due_date' => $invoice->due_date ?: '', + 'terms' => $invoice->terms ?: '', + 'public_notes' => $invoice->public_notes ?: '', + 'private_notes' => $invoice->private_notes ?: '', + 'is_deleted' => (bool) $invoice->is_deleted, + 'tax_name1' => $invoice->tax_name1 ? $invoice->tax_name1 : '', + 'tax_rate1' => (float) $invoice->tax_rate1, + 'tax_name2' => $invoice->tax_name2 ? $invoice->tax_name2 : '', + 'tax_rate2' => (float) $invoice->tax_rate2, + 'is_amount_discount' => (bool) ($invoice->is_amount_discount ?: false), + 'invoice_footer' => $invoice->invoice_footer ?: '', + 'partial' => (float) ($invoice->partial ?: 0.0), + 'partial_due_date' => $invoice->partial_due_date ?: '', + 'custom_value1' => (float) $invoice->custom_value1, + 'custom_value2' => (float) $invoice->custom_value2, + 'custom_taxes1' => (bool) $invoice->custom_taxes1, + 'custom_taxes2' => (bool) $invoice->custom_taxes2, + 'has_tasks' => (bool) $invoice->has_tasks, + 'has_expenses' => (bool) $invoice->has_expenses, + 'custom_text_value1' => $invoice->custom_text_value1 ?: '', + 'custom_text_value2' => $invoice->custom_text_value2 ?: '', + 'backup' => $invoice->backup ?: '', + 'settings' => $invoice->settings, + 'frequency_id' => (int) $invoice->frequency_id, + 'start_date' => $invoice->start_date, + 'last_sent_date' => $invoice->last_sent_date, + 'next_send_date' => $invoice->next_send_date, + 'remaining_cycles' => (int) $invoice->remaining_cycles, + ]; + } +} diff --git a/database/factories/RecurringInvoiceFactory.php b/database/factories/RecurringInvoiceFactory.php new file mode 100644 index 000000000000..cb735c181f8c --- /dev/null +++ b/database/factories/RecurringInvoiceFactory.php @@ -0,0 +1,32 @@ +define(App\Models\RecurringInvoice::class, function (Faker $faker) { + return [ + 'status_id' => App\Models\RecurringInvoice::STATUS_PENDING, + 'discount' => $faker->numberBetween(1,10), + 'is_amount_discount' => $faker->boolean(), + 'tax_name1' => 'GST', + 'tax_rate1' => 10, + 'tax_name2' => 'VAT', + 'tax_rate2' => 17.5, + 'custom_value1' => $faker->numberBetween(1,4), + 'custom_value2' => $faker->numberBetween(1,4), + 'custom_value3' => $faker->numberBetween(1,4), + 'custom_value4' => $faker->numberBetween(1,4), + 'is_deleted' => false, + 'po_number' => $faker->text(10), + 'invoice_date' => $faker->date(), + 'due_date' => $faker->date(), + 'line_items' => false, + 'backup' => '', + 'frequency_id' => App\Models\RecurringInvoice::FREQUENCY_MONTHLY, + 'start_date' => $faker->date(), + 'last_sent_date' => $faker->date(), + 'next_send_date' => $faker->date(), + 'remaining_cycles' => $faker->numberBetween(1,10), + ]; +}); \ No newline at end of file diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index 71145e854616..2c1bbab9f132 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -418,7 +418,8 @@ class CreateUsersTable extends Migration $t->unsignedInteger('user_id'); $t->unsignedInteger('company_id')->index(); - $t->string('invoice_number'); + $t->unsignedInteger('status_id')->index(); + $t->float('discount'); $t->boolean('is_amount_discount'); @@ -469,7 +470,6 @@ class CreateUsersTable extends Migration $t->timestamps(); $t->softDeletes(); - $t->unique(['company_id', 'invoice_number']); }); Schema::create('quotes', function ($t) { diff --git a/tests/Feature/RecurringInvoiceTest.php b/tests/Feature/RecurringInvoiceTest.php new file mode 100644 index 000000000000..8a3778e2383d --- /dev/null +++ b/tests/Feature/RecurringInvoiceTest.php @@ -0,0 +1,203 @@ +faker = \Faker\Factory::create(); + + Model::reguard(); + + + } + + public function testRecurringInvoiceList() + { + $data = [ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->unique()->safeEmail, + 'password' => 'ALongAndBrilliantPassword123', + '_token' => csrf_token(), + 'privacy_policy' => 1, + 'terms_of_service' => 1 + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + ])->post('/api/v1/signup', $data); + + $acc = $response->json(); + + $account = Account::find($this->decodePrimaryKey($acc['data']['id'])); + + $company_token = $account->default_company->tokens()->first(); + $token = $company_token->token; + $company = $company_token->company; + + $user = $company_token->user; + + $this->assertNotNull($company_token); + $this->assertNotNull($token); + $this->assertNotNull($user); + $this->assertNotNull($company); + $this->assertNotNull($user->tokens->first()->company); + + factory(\App\Models\Client::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company){ + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id, + 'is_primary' => 1 + ]); + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id + ]); + + }); + $client = Client::all()->first(); + + factory(\App\Models\RecurringInvoice::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->get('/api/v1/recurring_invoices'); + + $response->assertStatus(200); + + } + + public function testRecurringInvoiceRESTEndPoints() + { + $data = [ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->unique()->safeEmail, + 'password' => 'ALongAndBrilliantPassword123', + '_token' => csrf_token(), + 'privacy_policy' => 1, + 'terms_of_service' => 1 + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + ])->post('/api/v1/signup', $data); + + $acc = $response->json(); + + $account = Account::find($this->decodePrimaryKey($acc['data']['id'])); + + $company_token = $account->default_company->tokens()->first(); + $token = $company_token->token; + $company = $company_token->company; + + $user = $company_token->user; + + $this->assertNotNull($company_token); + $this->assertNotNull($token); + $this->assertNotNull($user); + $this->assertNotNull($company); + $this->assertNotNull($user->tokens->first()->company); + + factory(\App\Models\Client::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company){ + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id, + 'is_primary' => 1 + ]); + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id + ]); + + }); + $client = Client::all()->first(); + + factory(\App\Models\RecurringInvoice::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]); + + $RecurringInvoice = RecurringInvoice::where('user_id',$user->id)->first(); + $RecurringInvoice->settings = $client->getMergedSettings(); + $RecurringInvoice->save(); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->get('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id)); + + $response->assertStatus(200); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->get('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id).'/edit'); + + $response->assertStatus(200); + + $RecurringInvoice_update = [ + 'status_id' => RecurringInvoice::STATUS_PAID + ]; + + $this->assertNotNull($RecurringInvoice); + $this->assertNotNull($RecurringInvoice->settings); + + $this->assertTrue(property_exists($RecurringInvoice->settings, 'custom_taxes1')); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update) + ->assertStatus(200); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->delete('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id)); + + $response->assertStatus(200); + + } + +}