diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 9da20b1020c9..c66cdc40e5a9 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -11,28 +11,29 @@ namespace App\Console; -use App\Jobs\Cron\AutoBillCron; -use App\Jobs\Cron\RecurringExpensesCron; -use App\Jobs\Cron\RecurringInvoicesCron; -use App\Jobs\Cron\SubscriptionCron; -use App\Jobs\Cron\UpdateCalculatedFields; -use App\Jobs\Invoice\InvoiceCheckLateWebhook; -use App\Jobs\Ninja\AdjustEmailQuota; -use App\Jobs\Ninja\BankTransactionSync; -use App\Jobs\Ninja\CompanySizeCheck; +use App\Utils\Ninja; +use App\Models\Account; use App\Jobs\Ninja\QueueSize; -use App\Jobs\Ninja\SystemMaintenance; -use App\Jobs\Ninja\TaskScheduler; -use App\Jobs\Quote\QuoteCheckExpired; -use App\Jobs\Subscription\CleanStaleInvoiceOrder; use App\Jobs\Util\DiskCleanup; use App\Jobs\Util\ReminderJob; -use App\Jobs\Util\SchedulerCheck; -use App\Jobs\Util\UpdateExchangeRates; +use App\Jobs\Cron\AutoBillCron; use App\Jobs\Util\VersionCheck; -use App\Models\Account; -use App\Utils\Ninja; +use App\Jobs\Ninja\TaskScheduler; +use App\Jobs\Util\SchedulerCheck; +use App\Jobs\Ninja\CheckACHStatus; +use App\Jobs\Cron\SubscriptionCron; +use App\Jobs\Ninja\AdjustEmailQuota; +use App\Jobs\Ninja\CompanySizeCheck; +use App\Jobs\Ninja\SystemMaintenance; +use App\Jobs\Quote\QuoteCheckExpired; +use App\Jobs\Util\UpdateExchangeRates; +use App\Jobs\Ninja\BankTransactionSync; +use App\Jobs\Cron\RecurringExpensesCron; +use App\Jobs\Cron\RecurringInvoicesCron; +use App\Jobs\Cron\UpdateCalculatedFields; use Illuminate\Console\Scheduling\Schedule; +use App\Jobs\Invoice\InvoiceCheckLateWebhook; +use App\Jobs\Subscription\CleanStaleInvoiceOrder; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; class Kernel extends ConsoleKernel @@ -109,6 +110,9 @@ class Kernel extends ConsoleKernel /* Pulls in bank transactions from third party services */ $schedule->job(new BankTransactionSync)->everyFourHours()->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer(); + /* Checks ACH verification status and updates state to authorize when verified */ + $schedule->job(new CheckACHStatus)->everySixHours()->withoutOverlapping()->name('ach-status-job')->onOneServer(); + $schedule->command('ninja:check-data --database=db-ninja-01')->dailyAt('02:10')->withoutOverlapping()->name('check-data-db-1-job')->onOneServer(); $schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer(); diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 99638a0fa981..e4e8ff3085c5 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -481,8 +481,11 @@ class CompanySettings extends BaseSettings public $enable_e_invoice = false; + public $classification = ''; // individual, company, partnership, trust, charity, government, other + public static $casts = [ 'enable_e_invoice' => 'bool', + 'classification' => 'string', 'default_expense_payment_type_id' => 'string', 'e_invoice_type' => 'string', 'mailgun_endpoint' => 'string', diff --git a/app/Factory/ClientFactory.php b/app/Factory/ClientFactory.php index 37d23311add5..fbb6f36ab528 100644 --- a/app/Factory/ClientFactory.php +++ b/app/Factory/ClientFactory.php @@ -32,7 +32,8 @@ class ClientFactory $client->is_deleted = 0; $client->client_hash = Str::random(40); $client->settings = ClientSettings::defaults(); - + $client->classification = ''; + return $client; } } diff --git a/app/Factory/VendorFactory.php b/app/Factory/VendorFactory.php index 021d91053c6e..04c009267b21 100644 --- a/app/Factory/VendorFactory.php +++ b/app/Factory/VendorFactory.php @@ -28,6 +28,7 @@ class VendorFactory $vendor->country_id = 4; $vendor->is_deleted = 0; $vendor->vendor_hash = Str::random(40); + $vendor->classification = ''; return $vendor; } diff --git a/app/Http/Controllers/CompanyController.php b/app/Http/Controllers/CompanyController.php index 016485535bc0..ede5c1b92d21 100644 --- a/app/Http/Controllers/CompanyController.php +++ b/app/Http/Controllers/CompanyController.php @@ -697,4 +697,19 @@ class CompanyController extends BaseController return $this->itemResponse($company->fresh()); } + + public function logo() + { + + /** @var \App\Models\User $user */ + $user = auth()->user(); + $company = $user->company(); + $logo = strlen($company->settings->company_logo) > 5 ? $company->settings->company_logo : 'https://pdf.invoicing.co/favicon-v2.png'; + $headers = ['Content-Disposition' => 'inline']; + + return response()->streamDownload(function () use ($logo){ + echo @file_get_contents($logo); + }, 'logo.png', $headers); + + } } diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index c06305dd8e77..3a1d3841bfc4 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -93,7 +93,8 @@ class StoreClientRequest extends Request $rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)]; $rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)]; - + $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; + return $rules; } diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index 0344d7aaf205..0c2f3628874f 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -60,6 +60,7 @@ class UpdateClientRequest extends Request $rules['size_id'] = 'integer|nullable'; $rules['country_id'] = 'integer|nullable'; $rules['shipping_country_id'] = 'integer|nullable'; + $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; if ($this->id_number) { $rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id); diff --git a/app/Http/Requests/Vendor/StoreVendorRequest.php b/app/Http/Requests/Vendor/StoreVendorRequest.php index 2de5edeca00c..3ce8ba472198 100644 --- a/app/Http/Requests/Vendor/StoreVendorRequest.php +++ b/app/Http/Requests/Vendor/StoreVendorRequest.php @@ -60,6 +60,7 @@ class StoreVendorRequest extends Request } $rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id'; + $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; return $rules; } diff --git a/app/Http/Requests/Vendor/UpdateVendorRequest.php b/app/Http/Requests/Vendor/UpdateVendorRequest.php index 867b4541c0bf..b50e8cf79229 100644 --- a/app/Http/Requests/Vendor/UpdateVendorRequest.php +++ b/app/Http/Requests/Vendor/UpdateVendorRequest.php @@ -61,6 +61,7 @@ class UpdateVendorRequest extends Request } $rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id'; + $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; return $rules; } diff --git a/app/Jobs/Ninja/CheckACHStatus.php b/app/Jobs/Ninja/CheckACHStatus.php new file mode 100644 index 000000000000..a7f61b25520c --- /dev/null +++ b/app/Jobs/Ninja/CheckACHStatus.php @@ -0,0 +1,79 @@ +where('created_at', '>', now()->subMonths(2)) + ->where('gateway_type_id', 2) + ->whereHas('gateway', function ($q) { + $q->whereIn('gateway_key', ['d14dd26a37cecc30fdd65700bfb55b23','d14dd26a47cecc30fdd65700bfb67b34']); + }) + ->whereJsonContains('meta', ['state' => 'unauthorized']) + ->cursor() + ->each(function ($token) { + + try { + $stripe = $token->gateway->driver($token->client)->init(); + $pm = $stripe->getStripePaymentMethod($token->token); + + if($pm) { + + $meta = $token->meta; + $meta->state = 'authorized'; + $token->meta = $meta; + $token->save(); + + } + + } catch (\Exception $e) { + } + + }); + } + } +} \ No newline at end of file diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index ad6074309f80..3c98c00a4387 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -11,22 +11,24 @@ namespace App\Jobs\RecurringInvoice; -use App\DataMapper\Analytics\SendRecurringFailure; -use App\Factory\InvoiceInvitationFactory; -use App\Factory\RecurringInvoiceToInvoiceFactory; -use App\Jobs\Cron\AutoBill; -use App\Jobs\Entity\EmailEntity; +use Carbon\Carbon; +use App\Utils\Ninja; use App\Models\Invoice; +use App\Jobs\Cron\AutoBill; +use Illuminate\Bus\Queueable; +use App\Utils\Traits\MakesHash; +use App\Jobs\Entity\EmailEntity; use App\Models\RecurringInvoice; use App\Utils\Traits\GeneratesCounter; -use App\Utils\Traits\MakesHash; -use Carbon\Carbon; -use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Turbo124\Beacon\Facades\LightLogs; +use Illuminate\Queue\InteractsWithQueue; +use App\Events\Invoice\InvoiceWasCreated; +use App\Factory\InvoiceInvitationFactory; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use App\Factory\RecurringInvoiceToInvoiceFactory; +use App\DataMapper\Analytics\SendRecurringFailure; class SendRecurring implements ShouldQueue { @@ -105,6 +107,7 @@ class SendRecurring implements ShouldQueue $this->recurring_invoice->save(); event('eloquent.created: App\Models\Invoice', $invoice); + event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars())); //auto bill, BUT NOT DRAFTS!! if ($invoice->auto_bill_enabled && $invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->client->getSetting('auto_email_invoice')) { diff --git a/app/Listeners/Invoice/CreateInvoiceActivity.php b/app/Listeners/Invoice/CreateInvoiceActivity.php index 42a5ccb34a19..f5baea05f0ea 100644 --- a/app/Listeners/Invoice/CreateInvoiceActivity.php +++ b/app/Listeners/Invoice/CreateInvoiceActivity.php @@ -52,7 +52,8 @@ class CreateInvoiceActivity implements ShouldQueue $fields->client_id = $event->invoice->client_id; $fields->company_id = $event->invoice->company_id; $fields->activity_type_id = Activity::CREATE_INVOICE; - + $fields->recurring_invoice_id = $event->invoice->recurring_id; + $this->activity_repo->save($fields, $event->invoice, $event->event_vars); } } diff --git a/app/Models/Activity.php b/app/Models/Activity.php index 2677a149d5c5..1b9d89ce59d7 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -416,6 +416,9 @@ class Activity extends StaticModel if($this->vendor) $replacements['vendor'] = ['label' => $this?->vendor?->present()->name() ?? '', 'hashed_id' => $this->vendor->hashed_id ?? '']; + if($this->activity_type_id == 4 && $this->recurring_invoice) + $replacements['recurring_invoice'] = ['label' => $this?->recurring_invoice?->number ?? '', 'hashed_id' => $this->recurring_invoice->hashed_id ?? '']; + $replacements['activity_type_id'] = $this->activity_type_id; $replacements['id'] = $this->id; $replacements['hashed_id'] = $this->hashed_id; diff --git a/app/Models/Client.php b/app/Models/Client.php index 2087a27186da..5abb007ad357 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -169,6 +169,7 @@ class Client extends BaseModel implements HasLocalePreference 'routing_id', 'is_tax_exempt', 'has_valid_vat_number', + 'classification', ]; protected $with = [ diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 3773e8237d99..6df4fbc0dbaf 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -361,6 +361,24 @@ class Payment extends BaseModel return new PaymentService($this); } + /** + * $data = [ + 'id' => $payment->id, + 'amount' => 10, + 'invoices' => [ + [ + 'invoice_id' => $invoice->id, + 'amount' => 10, + ], + ], + 'date' => '2020/12/12', + 'gateway_refund' => false, + 'email_receipt' => false, + ]; + * + * @param array $data + * @return self + */ public function refund(array $data) :self { return $this->service()->refundPayment($data); diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 25ebbc4d9e29..243a38691370 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -113,6 +113,7 @@ class Vendor extends BaseModel 'custom_value4', 'number', 'language_id', + 'classification', ]; protected $casts = [ diff --git a/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php b/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php new file mode 100644 index 000000000000..d0393dd2e752 --- /dev/null +++ b/app/PaymentDrivers/Stripe/Jobs/ChargeRefunded.php @@ -0,0 +1,147 @@ +stripe_request = $stripe_request; + $this->company_key = $company_key; + $this->company_gateway_id = $company_gateway_id; + } + + public function handle() + { + MultiDB::findAndSetDbByCompanyKey($this->company_key); + nlog($this->stripe_request); + + $company = Company::query()->where('company_key', $this->company_key)->first(); + + $source = $this->stripe_request['object']; + $charge_id = $source['id']; + $amount_refunded = $source['amount_refunded'] ?? 0; + + $payment_hash_key = $source['metadata']['payment_hash'] ?? null; + + $company_gateway = CompanyGateway::query()->find($this->company_gateway_id); + $payment_hash = PaymentHash::query()->where('hash', $payment_hash_key)->first(); + + $stripe_driver = $company_gateway->driver()->init(); + + $stripe_driver->payment_hash = $payment_hash; + + /** @var \App\Models\Payment $payment **/ + $payment = Payment::query() + ->withTrashed() + ->where('company_id', $company->id) + ->where('transaction_reference', $charge_id) + ->first(); + + //don't touch if already refunded + if(!$payment || in_array($payment->status_id, [Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])) { + return; + } + + $stripe_driver->client = $payment->client; + + $amount_refunded = $stripe_driver->convertFromStripeAmount($amount_refunded, $payment->client->currency()->precision, $payment->client->currency()); + + if ($payment->status_id == Payment::STATUS_PENDING) { + $payment->service()->deletePayment(); + $payment->status_id = Payment::STATUS_FAILED; + $payment->save(); + return; + } + + if($payment->status_id == Payment::STATUS_COMPLETED) { + + $invoice_collection = $payment->paymentables + ->where('paymentable_type','invoices') + ->map(function ($pivot){ + return [ + 'invoice_id' => $pivot->paymentable_id, + 'amount' => $pivot->amount - $pivot->refunded + ]; + }); + + if($invoice_collection->count() == 1 && $invoice_collection->first()['amount'] >= $amount_refunded) { + //If there is only one invoice- and we are refunding _less_ than the amount of the invoice, we can just refund the payment + + $invoice_collection = $payment->paymentables + ->where('paymentable_type', 'invoices') + ->map(function ($pivot) use ($amount_refunded){ + return [ + 'invoice_id' => $pivot->paymentable_id, + 'amount' => $amount_refunded + ]; + }); + + } + elseif($invoice_collection->sum('amount') != $amount_refunded) { + //too many edges cases at this point, return early + return; + } + + $invoices = $invoice_collection->toArray(); + + $data = [ + 'id' => $payment->id, + 'amount' => $amount_refunded, + 'invoices' => $invoices, + 'date' => now()->format('Y-m-d'), + 'gateway_refund' => false, + 'email_receipt' => false, + ]; + + nlog($data); + + $payment->refund($data); + + $payment->private_notes .= 'Refunded via Stripe'; + return; + } + + } + + public function middleware() + { + return [new WithoutOverlapping($this->company_gateway_id)]; + } +} diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index a80c09789dcd..de4f2ac93e14 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -39,6 +39,7 @@ use App\PaymentDrivers\Stripe\FPX; use App\PaymentDrivers\Stripe\GIROPAY; use App\PaymentDrivers\Stripe\iDeal; use App\PaymentDrivers\Stripe\ImportCustomers; +use App\PaymentDrivers\Stripe\Jobs\ChargeRefunded; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook; @@ -790,6 +791,12 @@ class StripePaymentDriver extends BaseDriver } elseif ($request->data['object']['status'] == "pending") { return response()->json([], 200); } + } elseif ($request->type === "charge.refunded") { + + ChargeRefunded::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(5, 10))); + + return response()->json([], 200); + } return response()->json([], 200); diff --git a/app/Transformers/ClientTransformer.php b/app/Transformers/ClientTransformer.php index f34b805ff581..711c9cfec5ba 100644 --- a/app/Transformers/ClientTransformer.php +++ b/app/Transformers/ClientTransformer.php @@ -151,6 +151,7 @@ class ClientTransformer extends EntityTransformer 'is_tax_exempt' => (bool) $client->is_tax_exempt, 'routing_id' => (string) $client->routing_id, 'tax_info' => $client->tax_data ?: new \stdClass, + 'classification' => $client->classification ?: '', ]; } } diff --git a/app/Transformers/VendorTransformer.php b/app/Transformers/VendorTransformer.php index 051e8fb2f497..29aeafa703cd 100644 --- a/app/Transformers/VendorTransformer.php +++ b/app/Transformers/VendorTransformer.php @@ -104,6 +104,7 @@ class VendorTransformer extends EntityTransformer 'created_at' => (int) $vendor->created_at, 'number' => (string) $vendor->number ?: '', 'language_id' => (string) $vendor->language_id ?: '', + 'classification' => (string) $vendor->classification ?: '', ]; } } diff --git a/database/migrations/2023_09_11_003230_add_client_and_company_classifications.php b/database/migrations/2023_09_11_003230_add_client_and_company_classifications.php new file mode 100644 index 000000000000..ba41f214219f --- /dev/null +++ b/database/migrations/2023_09_11_003230_add_client_and_company_classifications.php @@ -0,0 +1,31 @@ +string('classification')->nullable(); + }); + + Schema::table('vendors', function (Blueprint $table) { + $table->string('classification')->nullable(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/lang/en/texts.php b/lang/en/texts.php index b67f6f745b16..a7f79bf59196 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5158,6 +5158,12 @@ $LANG = array( 'click_or_drop_files_here' => 'Click or drop files here', 'set_public' => 'Set public', 'set_private' => 'Set private', + 'individual' => 'Individual', + 'business' => 'Business', + 'partnership' => 'partnership', + 'trust' => 'Trust', + 'charity' => 'Charity', + 'government' => 'Government', 'in_stock_quantity' => 'Stock quantity', ); diff --git a/routes/api.php b/routes/api.php index 3ce1fdc1737e..61e44f833902 100644 --- a/routes/api.php +++ b/routes/api.php @@ -177,6 +177,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('companies/purge_save_settings/{company}', [MigrationController::class, 'purgeCompanySaveSettings'])->middleware('password_protected'); Route::resource('companies', CompanyController::class); // name = (companies. index / create / show / update / destroy / edit + Route::get('companies/{company}/logo', [CompanyController::class, 'logo']); Route::put('companies/{company}/upload', [CompanyController::class, 'upload']); Route::post('companies/{company}/default', [CompanyController::class, 'default']); Route::post('companies/updateOriginTaxData/{company}', [CompanyController::class, 'updateOriginTaxData'])->middleware('throttle:3,1'); diff --git a/tests/Feature/ClassificationTest.php b/tests/Feature/ClassificationTest.php new file mode 100644 index 000000000000..fe9df9600756 --- /dev/null +++ b/tests/Feature/ClassificationTest.php @@ -0,0 +1,220 @@ +faker = \Faker\Factory::create(); + + $this->makeTestData(); + + + } + + public function testClientClassification() + { + $data = [ + 'name' => 'Personal Company', + 'classification' => 'individual' + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/clients', $data); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('individual', $arr['data']['classification']); + } + + public function testValidationClassification() + { + $data = [ + 'name' => 'Personal Company', + 'classification' => 'this_is_not_validated' + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/clients', $data); + + $response->assertStatus(422); + + } + + public function testValidation2Classification() + { + $this->client->classification = 'company'; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/clients/'.$this->client->hashed_id, $this->client->toArray()); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('company', $arr['data']['classification']); + } + + public function testValidation3Classification() + { + $this->client->classification = 'this_is_not_validated'; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/clients/'.$this->client->hashed_id, $this->client->toArray()); + + $response->assertStatus(422); + + } + + public function testVendorClassification() + { + $data = [ + 'name' => 'Personal Company', + 'classification' => 'individual' + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/vendors', $data); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('individual', $arr['data']['classification']); + } + + public function testVendorValidationClassification() + { + $data = [ + 'name' => 'Personal Company', + 'classification' => 'this_is_not_validated' + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/vendors', $data); + + $response->assertStatus(422); + + } + + public function testVendorValidation2Classification() + { + $this->vendor->classification = 'company'; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/vendors/'.$this->vendor->hashed_id, $this->vendor->toArray()); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('company', $arr['data']['classification']); + } + + public function testVendorValidation3Classification() + { + $this->vendor->classification = 'this_is_not_validated'; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/vendors/'.$this->vendor->hashed_id, $this->vendor->toArray()); + + $response->assertStatus(422); + + } + + public function testCompanyClassification() + { + $settings = $this->company->settings; + $settings->classification = 'company'; + + $this->company->settings = $settings; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/companies/'.$this->company->hashed_id, $this->company->toArray()); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('company', $arr['data']['settings']['classification']); + } + + public function testCompanyValidationClassification() + { + $settings = $this->company->settings; + $settings->classification = 545454; + + $this->company->settings = $settings; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/companies/'.$this->company->hashed_id, $this->company->toArray()); + + $response->assertStatus(422); + + } + + public function testCompanyValidation2Classification() + { + $settings = $this->company->settings; + $settings->classification = null; + + $this->company->settings = $settings; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->putJson('/api/v1/companies/'.$this->company->hashed_id, $this->company->toArray()); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals('', $arr['data']['settings']['classification']); + } +} \ No newline at end of file diff --git a/tests/Feature/CompanyTest.php b/tests/Feature/CompanyTest.php index 6d0fefbc2bf1..c3be4dfc2694 100644 --- a/tests/Feature/CompanyTest.php +++ b/tests/Feature/CompanyTest.php @@ -34,6 +34,8 @@ class CompanyTest extends TestCase use MockAccountData; use DatabaseTransactions; + public $faker; + protected function setUp() :void { parent::setUp(); @@ -47,6 +49,19 @@ class CompanyTest extends TestCase $this->makeTestData(); } + + public function testCompanyLogoInline() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson("/api/v1/companies/{$this->company->hashed_id}/logo"); + + $response->assertStatus(200); + $response->streamedContent(); + + } + public function testUpdateCompanyPropertyInvoiceTaskHours() { $company_update = [ @@ -56,9 +71,9 @@ class CompanyTest extends TestCase $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, - ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $company_update) - ->assertStatus(200); + ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $company_update); + $response->assertStatus(200); $arr = $response->json(); diff --git a/tests/MockUnitData.php b/tests/MockUnitData.php index 4ca402d23a6c..fcf7c3e1841f 100644 --- a/tests/MockUnitData.php +++ b/tests/MockUnitData.php @@ -11,15 +11,16 @@ namespace Tests; +use App\Models\User; +use App\Models\Client; +use App\Models\Vendor; +use App\Models\Account; +use App\Models\Company; +use App\Models\CompanyToken; +use App\Models\ClientContact; use App\DataMapper\CompanySettings; use App\DataMapper\DefaultSettings; use App\Factory\InvoiceItemFactory; -use App\Models\Account; -use App\Models\Client; -use App\Models\ClientContact; -use App\Models\Company; -use App\Models\CompanyToken; -use App\Models\User; /** * Class MockUnitData. @@ -34,6 +35,8 @@ trait MockUnitData public $client; + public $vendor; + public $faker; public $primary_contact; @@ -92,6 +95,11 @@ trait MockUnitData 'company_id' => $this->company->id, ]); + $this->vendor = Vendor::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + ]); + $this->primary_contact = ClientContact::factory()->create([ 'user_id' => $this->user->id, 'client_id' => $this->client->id,