diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php
index a3b5cf613ecd..ab73fd7bb3a3 100644
--- a/app/DataMapper/CompanySettings.php
+++ b/app/DataMapper/CompanySettings.php
@@ -613,7 +613,6 @@ class CompanySettings extends BaseSettings
'$total_taxes',
'$line_taxes',
'$subtotal',
- '$total',
'$discount',
'$custom_surcharge1',
'$custom_surcharge2',
@@ -621,6 +620,7 @@ class CompanySettings extends BaseSettings
'$custom_surcharge4',
'$paid_to_date',
'$client.balance',
+ '$total',
],
];
diff --git a/app/Factory/VendorFactory.php b/app/Factory/VendorFactory.php
index 6218200e3232..a618cfc89f80 100644
--- a/app/Factory/VendorFactory.php
+++ b/app/Factory/VendorFactory.php
@@ -24,13 +24,10 @@ class VendorFactory
$vendor->name = '';
$vendor->website = '';
$vendor->private_notes = '';
- $vendor->balance = 0;
- $vendor->paid_to_date = 0;
+ $vendor->public_notes = '';
$vendor->country_id = 4;
$vendor->is_deleted = 0;
-
- $vendor_contact = VendorContactFactory::create($company_id, $user_id);
- $vendor->contacts->add($vendor_contact);
+ $vendor->vendor_hash = Str::random(40);
return $vendor;
}
diff --git a/app/Http/Controllers/VendorController.php b/app/Http/Controllers/VendorController.php
index 495eab8eff41..c5bcd59a9a5c 100644
--- a/app/Http/Controllers/VendorController.php
+++ b/app/Http/Controllers/VendorController.php
@@ -11,7 +11,14 @@
namespace App\Http\Controllers;
+use App\Factory\VendorFactory;
use App\Filters\VendorFilters;
+use App\Http\Requests\Vendor\CreateVendorRequest;
+use App\Http\Requests\Vendor\DestroyVendorRequest;
+use App\Http\Requests\Vendor\EditVendorRequest;
+use App\Http\Requests\Vendor\ShowVendorRequest;
+use App\Http\Requests\Vendor\StoreVendorRequest;
+use App\Http\Requests\Vendor\UpdateVendorRequest;
use App\Jobs\Entity\ActionEntity;
use App\Jobs\Util\ProcessBulk;
use App\Jobs\Util\UploadAvatar;
@@ -31,7 +38,6 @@ use Illuminate\Support\Facades\Log;
/**
* Class VendorController.
- * @covers App\Http\Controllers\VendorController
*/
class VendorController extends BaseController
{
@@ -266,7 +272,7 @@ class VendorController extends BaseController
return $request->disallowUpdate();
}
- $vendor = $this->client_repo->save($request->all(), $vendor);
+ $vendor = $this->vendor_repo->save($request->all(), $vendor);
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
@@ -359,7 +365,7 @@ class VendorController extends BaseController
*/
public function store(StoreVendorRequest $request)
{
- $vendor = $this->client_repo->save($request->all(), VendorFactory::create(auth()->user()->company()->id, auth()->user()->id));
+ $vendor = $this->vendor_repo->save($request->all(), VendorFactory::create(auth()->user()->company()->id, auth()->user()->id));
$vendor->load('contacts', 'primary_contact');
@@ -485,7 +491,7 @@ class VendorController extends BaseController
$vendors->each(function ($vendor, $key) use ($action) {
if (auth()->user()->can('edit', $vendor)) {
- $this->client_repo->{$action}($vendor);
+ $this->vendor_repo->{$action}($vendor);
}
});
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 4f69d563d31b..5a5f3802fae9 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -56,7 +56,7 @@ class Kernel extends HttpKernel
'bindings',
'query_logging',
\App\Http\Middleware\StartupCheck::class,
-// \App\Http\Middleware\Cors::class,
+ \App\Http\Middleware\Cors::class,
],
'contact' => [
'throttle:60,1',
diff --git a/app/Http/Livewire/RecurringInvoicesTable.php b/app/Http/Livewire/RecurringInvoicesTable.php
index 725e861bc4d4..29b548c2aef4 100644
--- a/app/Http/Livewire/RecurringInvoicesTable.php
+++ b/app/Http/Livewire/RecurringInvoicesTable.php
@@ -19,7 +19,7 @@ class RecurringInvoicesTable extends Component
$query = $query
->where('client_id', auth('contact')->user()->client->id)
- ->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_COMPLETED])
+ ->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_PAUSED,RecurringInvoice::STATUS_COMPLETED])
->orderBy('status_id', 'asc')
->with('client')
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
diff --git a/app/Http/Middleware/Cors.php b/app/Http/Middleware/Cors.php
index d471371215bb..86f10e00493a 100644
--- a/app/Http/Middleware/Cors.php
+++ b/app/Http/Middleware/Cors.php
@@ -16,7 +16,7 @@ class Cors
// ALLOW OPTIONS METHOD
$headers = [
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
- 'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range',
+ 'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range',
];
return Response::make('OK', 200, $headers);
diff --git a/app/Http/Requests/Vendor/StoreVendorRequest.php b/app/Http/Requests/Vendor/StoreVendorRequest.php
index c92c52d65c4a..005fcea8a564 100644
--- a/app/Http/Requests/Vendor/StoreVendorRequest.php
+++ b/app/Http/Requests/Vendor/StoreVendorRequest.php
@@ -42,27 +42,24 @@ class StoreVendorRequest extends Request
//$rules['settings'] = new ValidVendorGroupSettingsRule();
$rules['contacts.*.email'] = 'nullable|distinct';
- $contacts = request('contacts');
+ // $contacts = request('contacts');
- if (is_array($contacts)) {
- for ($i = 0; $i < count($contacts); $i++) {
+ // if (is_array($contacts)) {
+ // for ($i = 0; $i < count($contacts); $i++) {
- //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
- }
- }
+ // //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
+ // }
+ // }
return $rules;
}
protected function prepareForValidation()
{
- $input = $this->all();
+ // $input = $this->all();
- if (! isset($input['settings'])) {
- $input['settings'] = VendorSettings::defaults();
- }
- $this->replace($input);
+ // $this->replace($input);
}
public function messages()
diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php
index 64de0049e65f..7aadf906fea5 100644
--- a/app/Models/RecurringInvoice.php
+++ b/app/Models/RecurringInvoice.php
@@ -36,11 +36,11 @@ class RecurringInvoice extends BaseModel
/**
* Invoice Statuses.
*/
- const STATUS_DRAFT = 2;
- const STATUS_ACTIVE = 3;
- const STATUS_CANCELLED = 4;
+ const STATUS_DRAFT = 1;
+ const STATUS_ACTIVE = 2;
+ const STATUS_PAUSED = 3;
+ const STATUS_COMPLETED = 4;
const STATUS_PENDING = -1;
- const STATUS_COMPLETED = -2;
/**
* Recurring intervals //todo MAP WHEN WE MIGRATE.
@@ -102,6 +102,7 @@ class RecurringInvoice extends BaseModel
'frequency_id',
'next_send_date',
'remaining_cycles',
+ 'auto_bill',
];
protected $casts = [
@@ -189,13 +190,10 @@ class RecurringInvoice extends BaseModel
public function getStatusAttribute()
{
- if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) { //marked as active, but yet to fire first cycle
+ if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now())
return self::STATUS_PENDING;
- } elseif ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) {
- return self::STATUS_COMPLETED;
- } else {
+ else
return $this->status_id;
- }
}
public function nextSendDate() :?Carbon
@@ -288,16 +286,16 @@ class RecurringInvoice extends BaseModel
return '
'.ctrans('texts.draft').'
';
break;
case self::STATUS_PENDING:
- return ''.ctrans('texts.sent').'
';
+ return ''.ctrans('texts.pending').'
';
break;
case self::STATUS_ACTIVE:
- return ''.ctrans('texts.partial').'
';
+ return ''.ctrans('texts.active').'
';
break;
case self::STATUS_COMPLETED:
return ''.ctrans('texts.status_completed').'
';
break;
- case self::STATUS_CANCELLED:
- return ''.ctrans('texts.overdue').'
';
+ case self::STATUS_PAUSED:
+ return ''.ctrans('texts.paused').'
';
break;
default:
// code...
@@ -368,7 +366,7 @@ class RecurringInvoice extends BaseModel
/* Return early if nothing to send back! */
if( $this->status_id == self::STATUS_COMPLETED ||
$this->status_id == self::STATUS_DRAFT ||
- $this->status_id == self::STATUS_CANCELLED ||
+ $this->status_id == self::STATUS_PAUSED ||
$this->remaining_cycles == 0 ||
!$this->next_send_date) {
diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php
index 9aae462d21f0..8cd1a54d5686 100644
--- a/app/Models/Vendor.php
+++ b/app/Models/Vendor.php
@@ -13,6 +13,7 @@ namespace App\Models;
use App\Models\Filterable;
use App\Models\VendorContact;
+use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -21,7 +22,8 @@ class Vendor extends BaseModel
{
use SoftDeletes;
use Filterable;
-
+ use GeneratesCounter;
+
protected $fillable = [
'name',
'id_number',
@@ -34,6 +36,7 @@ class Vendor extends BaseModel
'postal_code',
'country_id',
'private_notes',
+ 'public_notes',
'currency_id',
'website',
'transaction_name',
@@ -63,6 +66,12 @@ class Vendor extends BaseModel
return self::class;
}
+ public function primary_contact()
+ {
+ return $this->hasMany(VendorContact::class)->where('is_primary', true);
+ }
+
+
public function documents()
{
return $this->morphMany(Document::class, 'documentable');
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 79542792c87c..ca68f008cef4 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -56,7 +56,8 @@ class AppServiceProvider extends ServiceProvider
public function boot()
{
Relation::morphMap([
- 'invoices' => \App\Models\Invoice::class,
+ 'invoices' => \App\Models\Invoice::class,
+ // 'credits' => \App\Models\Credit::class,
'proposals' => \App\Models\Proposal::class,
]);
diff --git a/app/Repositories/VendorContactRepository.php b/app/Repositories/VendorContactRepository.php
index ba831d338a50..f9aab86646eb 100644
--- a/app/Repositories/VendorContactRepository.php
+++ b/app/Repositories/VendorContactRepository.php
@@ -13,6 +13,7 @@ namespace App\Repositories;
use App\Models\Vendor;
use App\Models\VendorContact;
+use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
@@ -58,9 +59,17 @@ class VendorContactRepository extends BaseRepository
$update_contact->fill($contact);
+ if (array_key_exists('password', $contact) && strlen($contact['password']) > 1) {
+
+ $update_contact->password = Hash::make($contact['password']);
+
+ }
+
$update_contact->save();
});
+ $vendor->load('contacts');
+
//always made sure we have one blank contact to maintain state
if ($contacts->count() == 0) {
$new_contact = new VendorContact;
diff --git a/app/Services/Invoice/HandleReversal.php b/app/Services/Invoice/HandleReversal.php
index 5ee609a06086..7a991f80ba09 100644
--- a/app/Services/Invoice/HandleReversal.php
+++ b/app/Services/Invoice/HandleReversal.php
@@ -18,6 +18,7 @@ use App\Factory\InvoiceItemFactory;
use App\Factory\PaymentFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\Client;
+use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Paymentable;
@@ -55,7 +56,7 @@ class HandleReversal extends AbstractService
$total_paid = $this->invoice->amount - $this->invoice->balance;
/*Adjust payment applied and the paymentables to the correct amount */
- $paymentables = Paymentable::wherePaymentableType(Invoice::class)
+ $paymentables = Paymentable::wherePaymentableType('invoices')
->wherePaymentableId($this->invoice->id)
->get();
@@ -95,6 +96,23 @@ class HandleReversal extends AbstractService
$credit->service()->markSent()->save();
}
+ /*If there is a payment linked, then the credit needs to be linked back to that payment in case of refund*/
+ if($paymentables->count() > 0){
+
+ $payment = $paymentables->first()->payment;
+ $payment->credits()->save($credit);
+
+ $paymentable_credit = $payment->credits()
+ ->wherePaymentableType(Credit::class)
+ ->wherePaymentableId($credit->id)
+ ->first();
+
+ //harvest the credit record and add in the amount for the credit.
+ $paymentable_credit->pivot->amount = $total_paid;
+ $paymentable_credit->pivot->save();
+
+ }
+
/* Set invoice balance to 0 */
if ($this->invoice->balance != 0) {
$this->invoice->ledger()->updateInvoiceBalance($balance_remaining * -1, $notes)->save();
@@ -119,37 +137,4 @@ class HandleReversal extends AbstractService
//create a ledger row for this with the resulting Credit ( also include an explanation in the notes section )
}
- // public function run2()
- // {
-
- // /* Check again!! */
- // if (!$this->invoice->invoiceReversable($this->invoice)) {
- // return $this->invoice;
- // }
-
- // if($this->invoice->status_id == Invoice::STATUS_CANCELLED)
- // $this->invoice = $this->invoice->service()->reverseCancellation()->save();
-
- // //$balance_remaining = $this->invoice->balance;
-
- // //$total_paid = $this->invoice->amount - $this->invoice->balance;
-
- // /*Adjust payment applied and the paymentables to the correct amount */
- // $paymentables = Paymentable::wherePaymentableType(Invoice::class)
- // ->wherePaymentableId($this->invoice->id)
- // ->get();
-
- // $total_paid = 0;
-
- // $paymentables->each(function ($paymentable) use ($total_paid) {
-
- // $reversable_amount = $paymentable->amount - $paymentable->refunded;
- // $total_paid -= $reversable_amount;
- // $paymentable->amount = $paymentable->refunded;
- // $paymentable->save();
- // });
-
- // //Unwinding any payments made to this invoice
-
- // }
}
diff --git a/app/Transformers/VendorTransformer.php b/app/Transformers/VendorTransformer.php
index a5eb2a03aa3a..26cfc5e83c4b 100644
--- a/app/Transformers/VendorTransformer.php
+++ b/app/Transformers/VendorTransformer.php
@@ -76,6 +76,7 @@ class VendorTransformer extends EntityTransformer
'name' => $vendor->name ?: '',
'website' => $vendor->website ?: '',
'private_notes' => $vendor->private_notes ?: '',
+ 'public_notes' => $vendor->public_notes ?: '',
'last_login' => (int) $vendor->last_login,
'address1' => $vendor->address1 ?: '',
'address2' => $vendor->address2 ?: '',
diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php
index 3fd4467f7e73..8716eb374737 100644
--- a/app/Utils/Traits/GeneratesCounter.php
+++ b/app/Utils/Traits/GeneratesCounter.php
@@ -13,11 +13,13 @@ namespace App\Utils\Traits;
use App\Models\Client;
use App\Models\Credit;
+use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\Timezone;
+use App\Models\Vendor;
use Illuminate\Support\Carbon;
/**
@@ -238,6 +240,28 @@ trait GeneratesCounter
return $client_number;
}
+
+ /**
+ * Gets the next client number.
+ *
+ * @param \App\Models\Vendor $vendor The vendor
+ * @return string The next vendor number.
+ */
+ public function getNextVendorNumber(Vendor $vendor) :string
+ {
+ $this->resetCompanyCounters($vendor->company);
+
+ $counter = $vendor->company->settings->vendor_number_counter;
+ $setting_entity = $vendor->company->settings->vendor_number_counter;
+
+ $vendor_number = $this->checkEntityNumber(Vendor::class, $vendor, $counter, $vendor->company->settings->counter_padding, $vendor->company->settings->vendor_number_pattern);
+
+ $this->incrementCounter($vendor->company, 'vendor_number_counter');
+
+ return $vendor_number;
+ }
+
+
/**
* Determines if it has shared counter.
*
@@ -254,33 +278,29 @@ trait GeneratesCounter
* Checks that the number has not already been used.
*
* @param Collection $entity The entity ie App\Models\Client, Invoice, Quote etc
- * @param int $counter The counter
- * @param int $padding The padding
+ * @param int $counter The counter
+ * @param int $padding The padding
*
- * @return string The padded and prefixed invoice number
+ * @return string The padded and prefixed entity number
*/
- private function checkEntityNumber($class, $client, $counter, $padding, $pattern)
+ private function checkEntityNumber($class, $entity, $counter, $padding, $pattern)
{
$check = false;
do {
$number = $this->padCounter($counter, $padding);
- $number = $this->applyNumberPattern($client, $number, $pattern);
-
- if ($class == Invoice::class || $class == RecurringInvoice::class) {
- $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
- } elseif ($class == Client::class) {
- $check = $class::whereCompanyId($client->company_id)->whereIdNumber($number)->withTrashed()->first();
- } elseif ($class == Credit::class) {
- $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
- } elseif ($class == Quote::class) {
- $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
- } elseif ($class == Payment::class) {
- $check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
- }
+ $number = $this->applyNumberPattern($entity, $number, $pattern);
+ if ($class == Invoice::class || $class == RecurringInvoice::class)
+ $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first();
+ elseif ($class == Client::class || $class == Vendor::class)
+ $check = $class::whereCompanyId($entity->company_id)->whereIdNumber($number)->withTrashed()->first();
+ else
+ $check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first();
+
$counter++;
+
} while ($check);
return $number;
@@ -389,16 +409,74 @@ trait GeneratesCounter
$client->company->save();
}
+ private function resetCompanyCounters($company)
+ {
+ $timezone = Timezone::find($company->settings->timezone_id);
+
+ $reset_date = Carbon::parse($company->settings->reset_counter_date, $timezone->name);
+
+ if (! $reset_date->isToday() || ! $company->settings->reset_counter_date) {
+ return false;
+ }
+
+ switch ($company->reset_counter_frequency_id) {
+ case RecurringInvoice::FREQUENCY_WEEKLY:
+ $reset_date->addWeek();
+ break;
+ case RecurringInvoice::FREQUENCY_TWO_WEEKS:
+ $reset_date->addWeeks(2);
+ break;
+ case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
+ $reset_date->addWeeks(4);
+ break;
+ case RecurringInvoice::FREQUENCY_MONTHLY:
+ $reset_date->addMonth();
+ break;
+ case RecurringInvoice::FREQUENCY_TWO_MONTHS:
+ $reset_date->addMonths(2);
+ break;
+ case RecurringInvoice::FREQUENCY_THREE_MONTHS:
+ $reset_date->addMonths(3);
+ break;
+ case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
+ $reset_date->addMonths(4);
+ break;
+ case RecurringInvoice::FREQUENCY_SIX_MONTHS:
+ $reset_date->addMonths(6);
+ break;
+ case RecurringInvoice::FREQUENCY_ANNUALLY:
+ $reset_date->addYear();
+ break;
+ case RecurringInvoice::FREQUENCY_TWO_YEARS:
+ $reset_date->addYears(2);
+ break;
+ }
+
+ $settings = $company->settings;
+ $settings->reset_counter_date = $reset_date->format('Y-m-d');
+ $settings->invoice_number_counter = 1;
+ $settings->quote_number_counter = 1;
+ $settings->credit_number_counter = 1;
+ $settings->vendor_number_counter = 1;
+ $settings->ticket_number_counter = 1;
+ $settings->payment_number_counter = 1;
+ $settings->task_number_counter = 1;
+ $settings->expense_number_counter = 1;
+
+ $company->settings = $settings;
+ $company->save();
+ }
+
/**
- * { function_description }.
+ * Formats a entity number by pattern
*
- * @param \App\Models\Client $client The client
- * @param string $counter The counter
- * @param null|string $pattern The pattern
+ * @param \App\Models\BaseModel $entity The entity object
+ * @param string $counter The counter
+ * @param null|string $pattern The pattern
*
- * @return string ( description_of_the_return_value )
+ * @return string The formatted number pattern
*/
- private function applyNumberPattern(Client $client, string $counter, $pattern) :string
+ private function applyNumberPattern($entity, string $counter, $pattern) :string
{
if (! $pattern) {
return $counter;
@@ -417,7 +495,7 @@ trait GeneratesCounter
$replace[] = $counter;
if (strstr($pattern, '{$user_id}')) {
- $user_id = $client->user_id ? $client->user_id : 0;
+ $user_id = $entity->user_id ? $entity->user_id : 0;
$search[] = '{$user_id}';
$replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT);
}
@@ -429,24 +507,24 @@ trait GeneratesCounter
$search[] = $matches[0];
/* The following adjusts for the company timezone - may bork tests depending on the time of day the tests are run!!!!!!*/
- $date = Carbon::now($client->company->timezone()->name)->format($format);
+ $date = Carbon::now($entity->company->timezone()->name)->format($format);
$replace[] = str_replace($format, $date, $matches[1]);
}
$search[] = '{$custom1}';
- $replace[] = $client->custom_value1;
+ $replace[] = $entity->custom_value1;
$search[] = '{$custom2}';
- $replace[] = $client->custom_value2;
+ $replace[] = $entity->custom_value2;
$search[] = '{$custom3}';
- $replace[] = $client->custom_value3;
+ $replace[] = $entity->custom_value3;
$search[] = '{$custom4}';
- $replace[] = $client->custom_value4;
+ $replace[] = $entity->custom_value4;
$search[] = '{$id_number}';
- $replace[] = $client->id_number;
+ $replace[] = $entity->id_number;
return str_replace($search, $replace, $pattern);
}
diff --git a/database/migrations/2020_09_22_205113_id_number_fields_for_missing_entities.php b/database/migrations/2020_09_22_205113_id_number_fields_for_missing_entities.php
new file mode 100644
index 000000000000..a1e665959d6a
--- /dev/null
+++ b/database/migrations/2020_09_22_205113_id_number_fields_for_missing_entities.php
@@ -0,0 +1,45 @@
+string('number')->nullable();
+ });
+
+ Schema::table('tasks', function (Blueprint $table) {
+ $table->string('number')->nullable();
+ });
+
+ Schema::table('vendors', function (Blueprint $table) {
+ $table->text('vendor_hash')->nullable();
+ $table->text('public_notes')->nullable();
+ });
+
+ Schema::table('vendor_contacts', function (Blueprint $table) {
+ $table->boolean('send_email')->default(0);
+ });
+
+
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ //
+ }
+}
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index df2ec1ec2253..7f06753b9dc7 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -3272,4 +3272,5 @@ return [
'password_strength' => 'Password strength too weak',
'thanks' => 'Thanks',
+ 'paused' => 'Paused',
];
diff --git a/tests/Feature/VendorApiTest.php b/tests/Feature/VendorApiTest.php
new file mode 100644
index 000000000000..3ad6fdb38a84
--- /dev/null
+++ b/tests/Feature/VendorApiTest.php
@@ -0,0 +1,152 @@
+makeTestData();
+
+ Session::start();
+
+ $this->faker = \Faker\Factory::create();
+
+ Model::reguard();
+ }
+
+ public function testVendorPost()
+ {
+ $data = [
+ 'name' => $this->faker->firstName,
+ ];
+
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->post('/api/v1/vendors', $data);
+
+ $response->assertStatus(200);
+ }
+
+ public function testVendorPut()
+ {
+ $data = [
+ 'name' => $this->faker->firstName,
+ 'id_number' => 'Coolio',
+ ];
+
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data);
+
+ $response->assertStatus(200);
+ }
+
+ public function testVendorGet()
+ {
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->get('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id));
+
+ $response->assertStatus(200);
+ }
+
+ public function testVendorNotArchived()
+ {
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->get('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id));
+
+ $arr = $response->json();
+
+ $this->assertEquals(0, $arr['data']['archived_at']);
+ }
+
+ public function testVendorArchived()
+ {
+ $data = [
+ 'ids' => [$this->encodePrimaryKey($this->vendor->id)],
+ ];
+
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->post('/api/v1/vendors/bulk?action=archive', $data);
+
+ $arr = $response->json();
+
+ $this->assertNotNull($arr['data'][0]['archived_at']);
+ }
+
+ public function testVendorRestored()
+ {
+ $data = [
+ 'ids' => [$this->encodePrimaryKey($this->vendor->id)],
+ ];
+
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->post('/api/v1/vendors/bulk?action=restore', $data);
+
+ $arr = $response->json();
+
+ $this->assertEquals(0, $arr['data'][0]['archived_at']);
+ }
+
+ public function testVendorDeleted()
+ {
+ $data = [
+ 'ids' => [$this->encodePrimaryKey($this->vendor->id)],
+ ];
+
+ $response = $this->withHeaders([
+ 'X-API-SECRET' => config('ninja.api_secret'),
+ 'X-API-TOKEN' => $this->token,
+ ])->post('/api/v1/vendors/bulk?action=delete', $data);
+
+ $arr = $response->json();
+
+ $this->assertTrue($arr['data'][0]['is_deleted']);
+ }
+}
diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php
index a98e4c8fe504..c79c756d758c 100644
--- a/tests/MockAccountData.php
+++ b/tests/MockAccountData.php
@@ -65,6 +65,8 @@ trait MockAccountData
public $quote;
+ public $vendor;
+
public function makeTestData()
{
@@ -166,6 +168,26 @@ trait MockAccountData
'send_email' => true,
]);
+ $this->vendor = factory(\App\Models\Vendor::class)->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ ]);
+
+ $vendor_contact = factory(\App\Models\VendorContact::class)->create([
+ 'user_id' => $this->user->id,
+ 'vendor_id' => $this->vendor->id,
+ 'company_id' => $this->company->id,
+ 'is_primary' => 1,
+ 'send_email' => true,
+ ]);
+
+ $vendor_contact2 = factory(\App\Models\VendorContact::class)->create([
+ 'user_id' => $this->user->id,
+ 'vendor_id' => $this->vendor->id,
+ 'company_id' => $this->company->id,
+ 'send_email' => true,
+ ]);
+
// $rels = collect($contact, $contact2);
// $this->client->setRelation('contacts', $rels);
// $this->client->save();
diff --git a/tests/Unit/GeneratesCounterTest.php b/tests/Unit/GeneratesCounterTest.php
index fde2585866bb..da4d204b7a47 100644
--- a/tests/Unit/GeneratesCounterTest.php
+++ b/tests/Unit/GeneratesCounterTest.php
@@ -13,6 +13,7 @@ namespace Tests\Unit;
use App\DataMapper\ClientSettings;
use App\DataMapper\DefaultSettings;
use App\Factory\ClientFactory;
+use App\Factory\VendorFactory;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
@@ -293,6 +294,25 @@ class GeneratesCounterTest extends TestCase
$this->assertEquals($client_number, date('Y').'-'.str_pad($this->client->user_id, 2, '0', STR_PAD_LEFT).'-0002');
}
+ public function testVendorNumberPattern()
+ {
+ $settings = $this->company->settings;
+ $settings->vendor_number_pattern = '{$year}-{$user_id}-{$counter}';
+ $this->company->settings = $settings;
+ $this->company->save();
+
+ $vendor = VendorFactory::create($this->company->id, $this->user->id);
+ $vendor->save();
+
+ $vendor_number = $this->getNextVendorNumber($vendor);
+
+ $this->assertEquals($vendor_number, date('Y').'-'.str_pad($vendor->user_id, 2, '0', STR_PAD_LEFT).'-0001');
+
+ $vendor_number = $this->getNextVendorNumber($vendor);
+
+ $this->assertEquals($vendor_number, date('Y').'-'.str_pad($vendor->user_id, 2, '0', STR_PAD_LEFT).'-0002');
+ }
+
/*
public function testClientNextNumber()