mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-17 08:32:51 +01:00
Vendor API endpoints
This commit is contained in:
parent
10d62f23f8
commit
ab0ec5e2cb
@ -613,7 +613,6 @@ class CompanySettings extends BaseSettings
|
|||||||
'$total_taxes',
|
'$total_taxes',
|
||||||
'$line_taxes',
|
'$line_taxes',
|
||||||
'$subtotal',
|
'$subtotal',
|
||||||
'$total',
|
|
||||||
'$discount',
|
'$discount',
|
||||||
'$custom_surcharge1',
|
'$custom_surcharge1',
|
||||||
'$custom_surcharge2',
|
'$custom_surcharge2',
|
||||||
@ -621,6 +620,7 @@ class CompanySettings extends BaseSettings
|
|||||||
'$custom_surcharge4',
|
'$custom_surcharge4',
|
||||||
'$paid_to_date',
|
'$paid_to_date',
|
||||||
'$client.balance',
|
'$client.balance',
|
||||||
|
'$total',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -24,13 +24,10 @@ class VendorFactory
|
|||||||
$vendor->name = '';
|
$vendor->name = '';
|
||||||
$vendor->website = '';
|
$vendor->website = '';
|
||||||
$vendor->private_notes = '';
|
$vendor->private_notes = '';
|
||||||
$vendor->balance = 0;
|
$vendor->public_notes = '';
|
||||||
$vendor->paid_to_date = 0;
|
|
||||||
$vendor->country_id = 4;
|
$vendor->country_id = 4;
|
||||||
$vendor->is_deleted = 0;
|
$vendor->is_deleted = 0;
|
||||||
|
$vendor->vendor_hash = Str::random(40);
|
||||||
$vendor_contact = VendorContactFactory::create($company_id, $user_id);
|
|
||||||
$vendor->contacts->add($vendor_contact);
|
|
||||||
|
|
||||||
return $vendor;
|
return $vendor;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,14 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Factory\VendorFactory;
|
||||||
use App\Filters\VendorFilters;
|
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\Entity\ActionEntity;
|
||||||
use App\Jobs\Util\ProcessBulk;
|
use App\Jobs\Util\ProcessBulk;
|
||||||
use App\Jobs\Util\UploadAvatar;
|
use App\Jobs\Util\UploadAvatar;
|
||||||
@ -31,7 +38,6 @@ use Illuminate\Support\Facades\Log;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class VendorController.
|
* Class VendorController.
|
||||||
* @covers App\Http\Controllers\VendorController
|
|
||||||
*/
|
*/
|
||||||
class VendorController extends BaseController
|
class VendorController extends BaseController
|
||||||
{
|
{
|
||||||
@ -266,7 +272,7 @@ class VendorController extends BaseController
|
|||||||
return $request->disallowUpdate();
|
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);
|
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
|
||||||
|
|
||||||
@ -359,7 +365,7 @@ class VendorController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function store(StoreVendorRequest $request)
|
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');
|
$vendor->load('contacts', 'primary_contact');
|
||||||
|
|
||||||
@ -485,7 +491,7 @@ class VendorController extends BaseController
|
|||||||
|
|
||||||
$vendors->each(function ($vendor, $key) use ($action) {
|
$vendors->each(function ($vendor, $key) use ($action) {
|
||||||
if (auth()->user()->can('edit', $vendor)) {
|
if (auth()->user()->can('edit', $vendor)) {
|
||||||
$this->client_repo->{$action}($vendor);
|
$this->vendor_repo->{$action}($vendor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class Kernel extends HttpKernel
|
|||||||
'bindings',
|
'bindings',
|
||||||
'query_logging',
|
'query_logging',
|
||||||
\App\Http\Middleware\StartupCheck::class,
|
\App\Http\Middleware\StartupCheck::class,
|
||||||
// \App\Http\Middleware\Cors::class,
|
\App\Http\Middleware\Cors::class,
|
||||||
],
|
],
|
||||||
'contact' => [
|
'contact' => [
|
||||||
'throttle:60,1',
|
'throttle:60,1',
|
||||||
|
@ -19,7 +19,7 @@ class RecurringInvoicesTable extends Component
|
|||||||
|
|
||||||
$query = $query
|
$query = $query
|
||||||
->where('client_id', auth('contact')->user()->client->id)
|
->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')
|
->orderBy('status_id', 'asc')
|
||||||
->with('client')
|
->with('client')
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
|
@ -16,7 +16,7 @@ class Cors
|
|||||||
// ALLOW OPTIONS METHOD
|
// ALLOW OPTIONS METHOD
|
||||||
$headers = [
|
$headers = [
|
||||||
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
|
'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);
|
return Response::make('OK', 200, $headers);
|
||||||
|
19
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
19
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
@ -42,27 +42,24 @@ class StoreVendorRequest extends Request
|
|||||||
//$rules['settings'] = new ValidVendorGroupSettingsRule();
|
//$rules['settings'] = new ValidVendorGroupSettingsRule();
|
||||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||||
|
|
||||||
$contacts = request('contacts');
|
// $contacts = request('contacts');
|
||||||
|
|
||||||
if (is_array($contacts)) {
|
// if (is_array($contacts)) {
|
||||||
for ($i = 0; $i < count($contacts); $i++) {
|
// for ($i = 0; $i < count($contacts); $i++) {
|
||||||
|
|
||||||
//$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
|
// //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function prepareForValidation()
|
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()
|
public function messages()
|
||||||
|
@ -36,11 +36,11 @@ class RecurringInvoice extends BaseModel
|
|||||||
/**
|
/**
|
||||||
* Invoice Statuses.
|
* Invoice Statuses.
|
||||||
*/
|
*/
|
||||||
const STATUS_DRAFT = 2;
|
const STATUS_DRAFT = 1;
|
||||||
const STATUS_ACTIVE = 3;
|
const STATUS_ACTIVE = 2;
|
||||||
const STATUS_CANCELLED = 4;
|
const STATUS_PAUSED = 3;
|
||||||
|
const STATUS_COMPLETED = 4;
|
||||||
const STATUS_PENDING = -1;
|
const STATUS_PENDING = -1;
|
||||||
const STATUS_COMPLETED = -2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recurring intervals //todo MAP WHEN WE MIGRATE.
|
* Recurring intervals //todo MAP WHEN WE MIGRATE.
|
||||||
@ -102,6 +102,7 @@ class RecurringInvoice extends BaseModel
|
|||||||
'frequency_id',
|
'frequency_id',
|
||||||
'next_send_date',
|
'next_send_date',
|
||||||
'remaining_cycles',
|
'remaining_cycles',
|
||||||
|
'auto_bill',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
@ -189,13 +190,10 @@ class RecurringInvoice extends BaseModel
|
|||||||
|
|
||||||
public function getStatusAttribute()
|
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;
|
return self::STATUS_PENDING;
|
||||||
} elseif ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) {
|
else
|
||||||
return self::STATUS_COMPLETED;
|
|
||||||
} else {
|
|
||||||
return $this->status_id;
|
return $this->status_id;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function nextSendDate() :?Carbon
|
public function nextSendDate() :?Carbon
|
||||||
@ -288,16 +286,16 @@ class RecurringInvoice extends BaseModel
|
|||||||
return '<h4><span class="badge badge-light">'.ctrans('texts.draft').'</span></h4>';
|
return '<h4><span class="badge badge-light">'.ctrans('texts.draft').'</span></h4>';
|
||||||
break;
|
break;
|
||||||
case self::STATUS_PENDING:
|
case self::STATUS_PENDING:
|
||||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.sent').'</span></h4>';
|
return '<h4><span class="badge badge-primary">'.ctrans('texts.pending').'</span></h4>';
|
||||||
break;
|
break;
|
||||||
case self::STATUS_ACTIVE:
|
case self::STATUS_ACTIVE:
|
||||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.partial').'</span></h4>';
|
return '<h4><span class="badge badge-primary">'.ctrans('texts.active').'</span></h4>';
|
||||||
break;
|
break;
|
||||||
case self::STATUS_COMPLETED:
|
case self::STATUS_COMPLETED:
|
||||||
return '<h4><span class="badge badge-success">'.ctrans('texts.status_completed').'</span></h4>';
|
return '<h4><span class="badge badge-success">'.ctrans('texts.status_completed').'</span></h4>';
|
||||||
break;
|
break;
|
||||||
case self::STATUS_CANCELLED:
|
case self::STATUS_PAUSED:
|
||||||
return '<h4><span class="badge badge-danger">'.ctrans('texts.overdue').'</span></h4>';
|
return '<h4><span class="badge badge-danger">'.ctrans('texts.paused').'</span></h4>';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// code...
|
// code...
|
||||||
@ -368,7 +366,7 @@ class RecurringInvoice extends BaseModel
|
|||||||
/* Return early if nothing to send back! */
|
/* Return early if nothing to send back! */
|
||||||
if( $this->status_id == self::STATUS_COMPLETED ||
|
if( $this->status_id == self::STATUS_COMPLETED ||
|
||||||
$this->status_id == self::STATUS_DRAFT ||
|
$this->status_id == self::STATUS_DRAFT ||
|
||||||
$this->status_id == self::STATUS_CANCELLED ||
|
$this->status_id == self::STATUS_PAUSED ||
|
||||||
$this->remaining_cycles == 0 ||
|
$this->remaining_cycles == 0 ||
|
||||||
!$this->next_send_date) {
|
!$this->next_send_date) {
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Models\Filterable;
|
use App\Models\Filterable;
|
||||||
use App\Models\VendorContact;
|
use App\Models\VendorContact;
|
||||||
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
@ -21,7 +22,8 @@ class Vendor extends BaseModel
|
|||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use Filterable;
|
use Filterable;
|
||||||
|
use GeneratesCounter;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
'id_number',
|
'id_number',
|
||||||
@ -34,6 +36,7 @@ class Vendor extends BaseModel
|
|||||||
'postal_code',
|
'postal_code',
|
||||||
'country_id',
|
'country_id',
|
||||||
'private_notes',
|
'private_notes',
|
||||||
|
'public_notes',
|
||||||
'currency_id',
|
'currency_id',
|
||||||
'website',
|
'website',
|
||||||
'transaction_name',
|
'transaction_name',
|
||||||
@ -63,6 +66,12 @@ class Vendor extends BaseModel
|
|||||||
return self::class;
|
return self::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function primary_contact()
|
||||||
|
{
|
||||||
|
return $this->hasMany(VendorContact::class)->where('is_primary', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function documents()
|
public function documents()
|
||||||
{
|
{
|
||||||
return $this->morphMany(Document::class, 'documentable');
|
return $this->morphMany(Document::class, 'documentable');
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Repositories;
|
|||||||
|
|
||||||
use App\Models\Vendor;
|
use App\Models\Vendor;
|
||||||
use App\Models\VendorContact;
|
use App\Models\VendorContact;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,9 +59,17 @@ class VendorContactRepository extends BaseRepository
|
|||||||
|
|
||||||
$update_contact->fill($contact);
|
$update_contact->fill($contact);
|
||||||
|
|
||||||
|
if (array_key_exists('password', $contact) && strlen($contact['password']) > 1) {
|
||||||
|
|
||||||
|
$update_contact->password = Hash::make($contact['password']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$update_contact->save();
|
$update_contact->save();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$vendor->load('contacts');
|
||||||
|
|
||||||
//always made sure we have one blank contact to maintain state
|
//always made sure we have one blank contact to maintain state
|
||||||
if ($contacts->count() == 0) {
|
if ($contacts->count() == 0) {
|
||||||
$new_contact = new VendorContact;
|
$new_contact = new VendorContact;
|
||||||
|
@ -76,6 +76,7 @@ class VendorTransformer extends EntityTransformer
|
|||||||
'name' => $vendor->name ?: '',
|
'name' => $vendor->name ?: '',
|
||||||
'website' => $vendor->website ?: '',
|
'website' => $vendor->website ?: '',
|
||||||
'private_notes' => $vendor->private_notes ?: '',
|
'private_notes' => $vendor->private_notes ?: '',
|
||||||
|
'public_notes' => $vendor->public_notes ?: '',
|
||||||
'last_login' => (int) $vendor->last_login,
|
'last_login' => (int) $vendor->last_login,
|
||||||
'address1' => $vendor->address1 ?: '',
|
'address1' => $vendor->address1 ?: '',
|
||||||
'address2' => $vendor->address2 ?: '',
|
'address2' => $vendor->address2 ?: '',
|
||||||
|
@ -13,11 +13,13 @@ namespace App\Utils\Traits;
|
|||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
|
use App\Models\Expense;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Models\Timezone;
|
use App\Models\Timezone;
|
||||||
|
use App\Models\Vendor;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,6 +240,28 @@ trait GeneratesCounter
|
|||||||
return $client_number;
|
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.
|
* Determines if it has shared counter.
|
||||||
*
|
*
|
||||||
@ -254,33 +278,29 @@ trait GeneratesCounter
|
|||||||
* Checks that the number has not already been used.
|
* Checks that the number has not already been used.
|
||||||
*
|
*
|
||||||
* @param Collection $entity The entity ie App\Models\Client, Invoice, Quote etc
|
* @param Collection $entity The entity ie App\Models\Client, Invoice, Quote etc
|
||||||
* @param int $counter The counter
|
* @param int $counter The counter
|
||||||
* @param int $padding The padding
|
* @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;
|
$check = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$number = $this->padCounter($counter, $padding);
|
$number = $this->padCounter($counter, $padding);
|
||||||
|
|
||||||
$number = $this->applyNumberPattern($client, $number, $pattern);
|
$number = $this->applyNumberPattern($entity, $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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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++;
|
$counter++;
|
||||||
|
|
||||||
} while ($check);
|
} while ($check);
|
||||||
|
|
||||||
return $number;
|
return $number;
|
||||||
@ -389,16 +409,74 @@ trait GeneratesCounter
|
|||||||
$client->company->save();
|
$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 \App\Models\BaseModel $entity The entity object
|
||||||
* @param string $counter The counter
|
* @param string $counter The counter
|
||||||
* @param null|string $pattern The pattern
|
* @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) {
|
if (! $pattern) {
|
||||||
return $counter;
|
return $counter;
|
||||||
@ -417,7 +495,7 @@ trait GeneratesCounter
|
|||||||
$replace[] = $counter;
|
$replace[] = $counter;
|
||||||
|
|
||||||
if (strstr($pattern, '{$user_id}')) {
|
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}';
|
$search[] = '{$user_id}';
|
||||||
$replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT);
|
$replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
@ -429,24 +507,24 @@ trait GeneratesCounter
|
|||||||
$search[] = $matches[0];
|
$search[] = $matches[0];
|
||||||
|
|
||||||
/* The following adjusts for the company timezone - may bork tests depending on the time of day the tests are run!!!!!!*/
|
/* 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]);
|
$replace[] = str_replace($format, $date, $matches[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$search[] = '{$custom1}';
|
$search[] = '{$custom1}';
|
||||||
$replace[] = $client->custom_value1;
|
$replace[] = $entity->custom_value1;
|
||||||
|
|
||||||
$search[] = '{$custom2}';
|
$search[] = '{$custom2}';
|
||||||
$replace[] = $client->custom_value2;
|
$replace[] = $entity->custom_value2;
|
||||||
|
|
||||||
$search[] = '{$custom3}';
|
$search[] = '{$custom3}';
|
||||||
$replace[] = $client->custom_value3;
|
$replace[] = $entity->custom_value3;
|
||||||
|
|
||||||
$search[] = '{$custom4}';
|
$search[] = '{$custom4}';
|
||||||
$replace[] = $client->custom_value4;
|
$replace[] = $entity->custom_value4;
|
||||||
|
|
||||||
$search[] = '{$id_number}';
|
$search[] = '{$id_number}';
|
||||||
$replace[] = $client->id_number;
|
$replace[] = $entity->id_number;
|
||||||
|
|
||||||
return str_replace($search, $replace, $pattern);
|
return str_replace($search, $replace, $pattern);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class IdNumberFieldsForMissingEntities extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('expenses', function (Blueprint $table) {
|
||||||
|
$table->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()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
@ -3272,4 +3272,5 @@ return [
|
|||||||
'password_strength' => 'Password strength too weak',
|
'password_strength' => 'Password strength too weak',
|
||||||
|
|
||||||
'thanks' => 'Thanks',
|
'thanks' => 'Thanks',
|
||||||
|
'paused' => 'Paused',
|
||||||
];
|
];
|
||||||
|
152
tests/Feature/VendorApiTest.php
Normal file
152
tests/Feature/VendorApiTest.php
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\DataMapper\DefaultSettings;
|
||||||
|
use App\Models\Account;
|
||||||
|
use App\Models\Vendor;
|
||||||
|
use App\Models\VendorContact;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Faker\Factory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Tests\MockAccountData;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @covers App\Http\Controllers\VendorController
|
||||||
|
*/
|
||||||
|
class VendorApiTest extends TestCase
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
use DatabaseTransactions;
|
||||||
|
use MockAccountData;
|
||||||
|
|
||||||
|
public function setUp() :void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->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']);
|
||||||
|
}
|
||||||
|
}
|
@ -65,6 +65,8 @@ trait MockAccountData
|
|||||||
|
|
||||||
public $quote;
|
public $quote;
|
||||||
|
|
||||||
|
public $vendor;
|
||||||
|
|
||||||
public function makeTestData()
|
public function makeTestData()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -166,6 +168,26 @@ trait MockAccountData
|
|||||||
'send_email' => true,
|
'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);
|
// $rels = collect($contact, $contact2);
|
||||||
// $this->client->setRelation('contacts', $rels);
|
// $this->client->setRelation('contacts', $rels);
|
||||||
// $this->client->save();
|
// $this->client->save();
|
||||||
|
@ -13,6 +13,7 @@ namespace Tests\Unit;
|
|||||||
use App\DataMapper\ClientSettings;
|
use App\DataMapper\ClientSettings;
|
||||||
use App\DataMapper\DefaultSettings;
|
use App\DataMapper\DefaultSettings;
|
||||||
use App\Factory\ClientFactory;
|
use App\Factory\ClientFactory;
|
||||||
|
use App\Factory\VendorFactory;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Credit;
|
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');
|
$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()
|
public function testClientNextNumber()
|
||||||
|
Loading…
Reference in New Issue
Block a user