mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 13:12:50 +01:00
commit
413eadd6fb
@ -1 +1 @@
|
||||
5.7.42
|
||||
5.7.43
|
@ -41,6 +41,9 @@ class ExpenseFilters extends QueryFilters
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%')
|
||||
->orWhereHas('category', function ($q) use ($filter) {
|
||||
$q->where('name', 'like', '%'.$filter.'%');
|
||||
})
|
||||
->orWhereHas('vendor', function ($q) use ($filter) {
|
||||
$q->where('name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -154,23 +154,15 @@ class UserFilters extends QueryFilters
|
||||
$user = auth()->user();
|
||||
|
||||
if (in_array(self::STATUS_ACTIVE, $filters)) {
|
||||
$query = $query->orWhereHas('company_users', function ($q) use($user){
|
||||
$q->where('company_id', $user->company()->id)->whereNull('deleted_at');
|
||||
});
|
||||
$query->orWhereNull('deleted_at');
|
||||
}
|
||||
|
||||
if (in_array(self::STATUS_ARCHIVED, $filters)) {
|
||||
$query = $query->orWhereHas('company_users', function ($q) use($user){
|
||||
$q->where('company_id', $user->company()->id)->whereNotNull('deleted_at')->where('is_deleted', 0);
|
||||
});
|
||||
|
||||
$query->orWhereNotNull('deleted_at')->where('is_deleted', 0);
|
||||
}
|
||||
|
||||
if (in_array(self::STATUS_DELETED, $filters)) {
|
||||
$query = $query->orWhereHas('company_users', function ($q) use($user){
|
||||
$q->where('company_id', $user->company()->id)->where('is_deleted', 1);
|
||||
});
|
||||
|
||||
$query->orWhere('is_deleted', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1123,10 +1123,10 @@ class BaseController extends Controller
|
||||
$data['white_label'] = Ninja::isSelfHost() ? $account->isPaid() : false;
|
||||
|
||||
//pass referral code to front end
|
||||
$data['rc'] = request()->has('rc') ? request()->input('rc') : '';
|
||||
$data['build'] = request()->has('build') ? request()->input('build') : '';
|
||||
$data['login'] = request()->has('login') ? request()->input('login') : 'false';
|
||||
$data['signup'] = request()->has('signup') ? request()->input('signup') : 'false';
|
||||
$data['rc'] = request()->has('rc') && is_string(request()->input('rc')) ? request()->input('rc') : '';
|
||||
$data['build'] = request()->has('build') && is_string(request()->input('build')) ? request()->input('build') : '';
|
||||
$data['login'] = request()->has('login') && is_string(request()->input('input')) ? request()->input('login') : 'false';
|
||||
$data['signup'] = request()->has('signup') && is_string(request()->input('signup')) ? request()->input('signup') : 'false';
|
||||
$data['canvas_path'] = $canvas_path;
|
||||
|
||||
if (request()->session()->has('login')) {
|
||||
|
@ -181,6 +181,16 @@ class InvoiceController extends Controller
|
||||
->with('message', ctrans('texts.no_payable_invoices_selected'));
|
||||
}
|
||||
|
||||
//ensure all stale fees are removed.
|
||||
$invoices->each(function ($invoice) {
|
||||
$invoice->service()
|
||||
->markSent()
|
||||
->removeUnpaidGatewayFees()
|
||||
->save();
|
||||
});
|
||||
|
||||
$invoices = $invoices->fresh();
|
||||
|
||||
//iterate and sum the payable amounts either partial or balance
|
||||
$total = 0;
|
||||
foreach ($invoices as $invoice) {
|
||||
|
@ -11,27 +11,28 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Events\Expense\ExpenseWasCreated;
|
||||
use App\Events\Expense\ExpenseWasUpdated;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Account;
|
||||
use App\Models\Expense;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Filters\ExpenseFilters;
|
||||
use App\Http\Requests\Expense\CreateExpenseRequest;
|
||||
use App\Http\Requests\Expense\DestroyExpenseRequest;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use App\Repositories\ExpenseRepository;
|
||||
use App\Transformers\ExpenseTransformer;
|
||||
use App\Events\Expense\ExpenseWasCreated;
|
||||
use App\Events\Expense\ExpenseWasUpdated;
|
||||
use App\Http\Requests\Expense\BulkExpenseRequest;
|
||||
use App\Http\Requests\Expense\EditExpenseRequest;
|
||||
use App\Http\Requests\Expense\ShowExpenseRequest;
|
||||
use App\Http\Requests\Expense\StoreExpenseRequest;
|
||||
use App\Http\Requests\Expense\CreateExpenseRequest;
|
||||
use App\Http\Requests\Expense\UpdateExpenseRequest;
|
||||
use App\Http\Requests\Expense\UploadExpenseRequest;
|
||||
use App\Models\Account;
|
||||
use App\Models\Expense;
|
||||
use App\Repositories\ExpenseRepository;
|
||||
use App\Transformers\ExpenseTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Requests\Expense\DestroyExpenseRequest;
|
||||
|
||||
/**
|
||||
* Class ExpenseController.
|
||||
@ -321,7 +322,10 @@ class ExpenseController extends BaseController
|
||||
*/
|
||||
public function create(CreateExpenseRequest $request)
|
||||
{
|
||||
$expense = ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$expense = ExpenseFactory::create($user->company()->id, $user->id);
|
||||
|
||||
return $this->itemResponse($expense);
|
||||
}
|
||||
@ -366,9 +370,12 @@ class ExpenseController extends BaseController
|
||||
*/
|
||||
public function store(StoreExpenseRequest $request)
|
||||
{
|
||||
$expense = $this->expense_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
$expense = $this->expense_repo->save($request->all(), ExpenseFactory::create($user->company()->id, $user->id));
|
||||
|
||||
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars($user ? $user->id : null)));
|
||||
|
||||
event('eloquent.created: App\Models\Expense', $expense);
|
||||
|
||||
@ -481,20 +488,25 @@ class ExpenseController extends BaseController
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function bulk()
|
||||
public function bulk(BulkExpenseRequest $request)
|
||||
{
|
||||
$action = request()->input('action');
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$ids = request()->input('ids');
|
||||
$expenses = Expense::withTrashed()->find($this->transformKeys($ids));
|
||||
$expenses = Expense::withTrashed()->find($request->ids);
|
||||
|
||||
$expenses->each(function ($expense, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $expense)) {
|
||||
$this->expense_repo->{$action}($expense);
|
||||
if($request->action == 'bulk_categorize' && $user->can('edit', $expenses->first())) {
|
||||
$this->expense_repo->categorize($expenses, $request->category_id);
|
||||
$expenses = collect([]);
|
||||
}
|
||||
|
||||
$expenses->each(function ($expense) use ($request, $user) {
|
||||
if ($user->can('edit', $expense)) {
|
||||
$this->expense_repo->{$request->action}($expense);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(Expense::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
return $this->listResponse(Expense::withTrashed()->whereIn('id', $request->ids));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +55,9 @@ class ImportJsonController extends BaseController
|
||||
*/
|
||||
public function import(ImportJsonRequest $request)
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$file_location = $request->file('files')
|
||||
->storeAs(
|
||||
'migrations',
|
||||
@ -63,9 +66,9 @@ class ImportJsonController extends BaseController
|
||||
);
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $file_location, $request->except('files'))->onQueue('migration');
|
||||
CompanyImport::dispatch($user->company(), $user, $file_location, $request->except('files'))->onQueue('migration');
|
||||
} else {
|
||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $file_location, $request->except('files'));
|
||||
CompanyImport::dispatch($user->company(), $user, $file_location, $request->except('files'));
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
@ -27,7 +27,7 @@ class SubdomainController extends BaseController
|
||||
public function index()
|
||||
{
|
||||
if (!MultiDB::checkDomainAvailable(request()->input('subdomain'))) {
|
||||
return response()->json(['message' => 'Domain not available'], 401);
|
||||
return response()->json(['message' => ctrans('texts.subdomain_is_not_available')], 401);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Domain available'], 200);
|
||||
|
@ -12,13 +12,10 @@
|
||||
namespace App\Http\Requests\Expense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Expense;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class BulkExpenseRequest extends Request
|
||||
{
|
||||
use BulkOptions;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@ -26,15 +23,7 @@ class BulkExpenseRequest extends Request
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
if (! $this->has('action')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! in_array($this->action, $this->getBulkOptions(), true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return auth()->user()->can(auth()->user()->isAdmin(), Expense::class);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,13 +33,30 @@ class BulkExpenseRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = $this->getGlobalRules();
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/* We don't require IDs on bulk storing. */
|
||||
if ($this->action !== self::$STORE_METHOD) {
|
||||
$rules['ids'] = ['required'];
|
||||
return [
|
||||
'ids' => ['required','bail','array', Rule::exists('expenses', 'id')->where('company_id', $user->company()->id)],
|
||||
'category_id' => ['sometimes', 'bail', Rule::exists('expense_categories', 'id')->where('company_id', $user->company()->id)],
|
||||
'action' => 'in:archive,restore,delete,bulk_categorize',
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (isset($input['ids'])) {
|
||||
$input['ids'] = $this->transformKeys($input['ids']);
|
||||
}
|
||||
|
||||
return $rules;
|
||||
if (isset($input['category_id'])) {
|
||||
$input['category_id'] = $this->transformKeys($input['category_id']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,8 @@ class StoreRecurringInvoiceRequest extends Request
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
$input['amount'] = 0;
|
||||
$input['balance'] = 0;
|
||||
|
||||
if (array_key_exists('due_date_days', $input) && is_null($input['due_date_days'])) {
|
||||
$input['due_date_days'] = 'terms';
|
||||
|
@ -25,7 +25,10 @@ class StoreSchedulerRequest extends Request
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
|
@ -22,7 +22,10 @@ class UpdateSchedulerRequest extends Request
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin() && $this->task_scheduler->company_id == auth()->user()->company()->id;
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->isAdmin() && $this->task_scheduler->company_id == $user->company()->id;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
|
@ -28,7 +28,10 @@ class ValidClientIds implements Rule
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
return Client::where('company_id', auth()->user()->company()->id)->whereIn('id', $this->transformKeys($value))->count() == count($value);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return Client::where('company_id', $user->company()->id)->whereIn('id', $this->transformKeys($value))->count() == count($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -201,7 +201,7 @@ class CompanyExport implements ShouldQueue
|
||||
return $document->makeVisible(['id']);
|
||||
})->all();
|
||||
|
||||
$this->export_data['expense_categories'] = $this->company->expense_categories->map(function ($expense_category) {
|
||||
$this->export_data['expense_categories'] = $this->company->expense_categories()->cursor()->map(function ($expense_category) {
|
||||
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
|
||||
|
||||
return $expense_category->makeVisible(['id']);
|
||||
@ -399,6 +399,12 @@ class CompanyExport implements ShouldQueue
|
||||
return $bank_transaction->makeVisible(['id','user_id','company_id']);
|
||||
})->all();
|
||||
|
||||
$this->export_data['schedulers'] = $this->company->schedulers()->orderBy('id', 'ASC')->cursor()->map(function ($scheduler) {
|
||||
$scheduler = $this->transformArrayOfKeys($scheduler, ['company_id', 'user_id']);
|
||||
|
||||
return $scheduler->makeVisible(['id','user_id','company_id']);
|
||||
})->all();
|
||||
|
||||
//write to tmp and email to owner();
|
||||
|
||||
$this->zipAndSend();
|
||||
@ -440,7 +446,7 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
$path = 'backups';
|
||||
|
||||
Storage::makeDirectory(storage_path('backups/'));
|
||||
// Storage::makeDirectory(storage_path('backups/'));
|
||||
|
||||
try {
|
||||
mkdir(storage_path('backups/'));
|
||||
|
@ -16,6 +16,7 @@ use App\Exceptions\NonExistingMigrationFile;
|
||||
use App\Factory\ClientContactFactory;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\Ninja\TaskScheduler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Import\CompanyImportFailure;
|
||||
use App\Mail\Import\ImportCompleted;
|
||||
@ -111,6 +112,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
private $file_path;
|
||||
|
||||
private string $import_version = '';
|
||||
|
||||
private $importables = [
|
||||
// 'company',
|
||||
'users',
|
||||
@ -151,6 +154,7 @@ class CompanyImport implements ShouldQueue
|
||||
'bank_integrations',
|
||||
'bank_transactions',
|
||||
'payments',
|
||||
'schedulers',
|
||||
];
|
||||
|
||||
private $company_properties = [
|
||||
@ -210,6 +214,22 @@ class CompanyImport implements ShouldQueue
|
||||
"convert_rate_to_client",
|
||||
];
|
||||
|
||||
private array $version_keys = [
|
||||
'baseline' => [],
|
||||
'5.7.35' => [
|
||||
Payment::class => [
|
||||
'refund_meta',
|
||||
'category_id',
|
||||
],
|
||||
User::class => [
|
||||
'user_logged_in_notification',
|
||||
],
|
||||
Design::class => [
|
||||
'is_template',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
@ -407,9 +427,7 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$data = (object)$this->getObject('app_version', true);
|
||||
|
||||
if ($this->current_app_version != $data->app_version) {
|
||||
//perform some magic here
|
||||
}
|
||||
$this->import_version = $data->app_version;
|
||||
|
||||
if ($this->pre_flight_checks_pass === false) {
|
||||
$this->sendImportMail($this->message);
|
||||
@ -439,10 +457,12 @@ class CompanyImport implements ShouldQueue
|
||||
$settings->project_number_counter = 1;
|
||||
$settings->purchase_order_number_counter = 1;
|
||||
$this->company->settings = $co->settings;
|
||||
// $this->company->settings = $this->backup_file->company->settings;
|
||||
|
||||
$this->company->saveSettings($co->settings, $this->company);
|
||||
|
||||
$this->company->save();
|
||||
|
||||
return $this;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function purgeCompanyData()
|
||||
@ -456,6 +476,10 @@ class CompanyImport implements ShouldQueue
|
||||
$this->company->expenses()->forceDelete();
|
||||
$this->company->subscriptions()->forceDelete();
|
||||
$this->company->purchase_orders()->forceDelete();
|
||||
$this->company->bank_integrations()->forceDelete();
|
||||
$this->company->bank_transactions()->forceDelete();
|
||||
$this->company->schedulers()->forceDelete();
|
||||
$this->company->system_log_relation()->forceDelete();
|
||||
|
||||
$this->company->save();
|
||||
|
||||
@ -507,6 +531,20 @@ class CompanyImport implements ShouldQueue
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function import_schedulers()
|
||||
{
|
||||
$this->genericNewClassImport(
|
||||
\App\Models\Scheduler::class,
|
||||
['company_id', 'id', 'hashed_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
],
|
||||
'schedulers'
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function import_bank_integrations()
|
||||
{
|
||||
$this->genericImport(
|
||||
@ -1132,7 +1170,6 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$new_document->save(['timestamps' => false]);
|
||||
|
||||
|
||||
$storage_url = (object)$this->getObject('storage_url', true);
|
||||
|
||||
if (!Storage::exists($new_document->url) && is_string($storage_url)) {
|
||||
@ -1327,6 +1364,35 @@ class CompanyImport implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
private function filterVersionProps($class, array $obj_array): array
|
||||
{
|
||||
|
||||
if($this->current_app_version == $this->import_version)
|
||||
return $obj_array;
|
||||
|
||||
$version_index = 0;
|
||||
$index = 0;
|
||||
|
||||
$filters = collect($this->version_keys)
|
||||
->map(function ($value, $key) use (&$version_index, &$index) {
|
||||
if($this->import_version == $key) {
|
||||
$version_index = $index;
|
||||
}
|
||||
|
||||
$index++;
|
||||
return $value;
|
||||
|
||||
})
|
||||
->when($version_index == 0, function ($collection) {
|
||||
return collect([]);
|
||||
})
|
||||
->when($version_index > 0, function ($collection) use (&$version_index, $class) {
|
||||
return $collection->slice($version_index)->pluck($class)->filter();
|
||||
});
|
||||
|
||||
return collect($obj_array)->diffKeys($filters->flatten()->flip())->toArray();
|
||||
|
||||
}
|
||||
|
||||
private function genericNewClassImport($class, $unset, $transforms, $object_property)
|
||||
{
|
||||
@ -1348,6 +1414,10 @@ class CompanyImport implements ShouldQueue
|
||||
if (Ninja::isSelfHost() && $obj_array['gateway_key'] == 'd14dd26a47cecc30fdd65700bfb67b34') {
|
||||
$obj_array['gateway_key'] = 'd14dd26a37cecc30fdd65700bfb55b23';
|
||||
}
|
||||
|
||||
if(!isset($obj_array['fees_and_limits'])){
|
||||
$obj_array['fees_and_limits'] = \json_encode([]);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('deleted_at', $obj_array) && $obj_array['deleted_at'] > 1) {
|
||||
@ -1385,8 +1455,29 @@ class CompanyImport implements ShouldQueue
|
||||
$obj_array['config'] = encrypt($obj_array['config']);
|
||||
}
|
||||
|
||||
if($class == 'App\Models\Scheduler') {
|
||||
$parameters = $obj_array['parameters'];
|
||||
|
||||
if(isset($parameters->clients)){
|
||||
|
||||
$parameters->clients =
|
||||
collect($parameters->clients)->map(function ($client_hash){
|
||||
return $this->encodePrimaryKey($this->transformId('clients', $client_hash));
|
||||
})->toArray();
|
||||
|
||||
}
|
||||
|
||||
if(isset($parameters->entity_id)){
|
||||
$parameters->entity_id = $this->encodePrimaryKey($this->transformId($parameters->entity."s", $parameters->entity_id));
|
||||
}
|
||||
|
||||
$obj_array['parameters'] = $parameters;
|
||||
}
|
||||
|
||||
$new_obj = new $class();
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$obj_array = $this->filterVersionProps($class,$obj_array);
|
||||
|
||||
$new_obj->fill($obj_array);
|
||||
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
@ -1431,6 +1522,8 @@ class CompanyImport implements ShouldQueue
|
||||
$obj_array['webhook_configuration'] = \json_encode($obj_array['webhook_configuration']);
|
||||
}
|
||||
|
||||
$obj_array = $this->filterVersionProps($class, $obj_array);
|
||||
|
||||
$new_obj = $class::firstOrNew(
|
||||
[$match_key => $obj->{$match_key}],
|
||||
$obj_array,
|
||||
@ -1482,6 +1575,8 @@ class CompanyImport implements ShouldQueue
|
||||
$obj_array['product_ids'] = '';
|
||||
}
|
||||
|
||||
$obj_array = $this->filterVersionProps($class, $obj_array);
|
||||
|
||||
/* Expenses that don't have a number will not be inserted - so need to override here*/
|
||||
if ($class == 'App\Models\Expense' && is_null($obj->{$match_key})) {
|
||||
$new_obj = new Expense();
|
||||
|
@ -743,12 +743,26 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
}
|
||||
|
||||
$invoices_string = str_replace(["*","<",">","'",'"'], "-", $invoices_string);
|
||||
// $invoices_string = "I-".$invoices_string;
|
||||
// $invoices_string = substr($invoices_string, 0, 22);
|
||||
|
||||
$invoices_string = "I-".$invoices_string;
|
||||
// 2023-11-02 - improve the statement descriptor for string
|
||||
$company_name = $this->client->company->present()->name();
|
||||
|
||||
$invoices_string = substr($invoices_string, 0, 22);
|
||||
if(ctype_digit(substr($company_name, 0, 1)))
|
||||
$company_name = "X" . $company_name;
|
||||
|
||||
$invoices_string = str_pad($invoices_string, 5, ctrans('texts.invoice'), STR_PAD_LEFT);
|
||||
$suffix = strlen($invoices_string) + 1;
|
||||
|
||||
$length = 22 - $suffix;
|
||||
|
||||
$company_name = substr($company_name, 0, $length);
|
||||
|
||||
$descriptor = "{$company_name} {$invoices_string}";
|
||||
|
||||
$invoices_string = str_pad($descriptor, 5, ctrans('texts.invoice'), STR_PAD_RIGHT);
|
||||
|
||||
// $invoices_string = str_pad($invoices_string, 5, ctrans('texts.invoice'), STR_PAD_LEFT);
|
||||
|
||||
return $invoices_string;
|
||||
|
||||
|
@ -51,34 +51,6 @@ class DirectDebit implements MethodInterface
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
return $this->billingRequestFlows($data);
|
||||
// $session_token = \Illuminate\Support\Str::uuid()->toString();
|
||||
|
||||
// try {
|
||||
// $redirect = $this->go_cardless->gateway->redirectFlows()->create([
|
||||
// 'params' => [
|
||||
// 'session_token' => $session_token,
|
||||
// 'success_redirect_url' => route('client.payment_methods.confirm', [
|
||||
// 'method' => GatewayType::DIRECT_DEBIT,
|
||||
// 'session_token' => $session_token,
|
||||
// ]),
|
||||
// 'prefilled_customer' => [
|
||||
// 'given_name' => auth()->guard('contact')->user()->first_name ?: '',
|
||||
// 'family_name' => auth()->guard('contact')->user()->last_name ?: '',
|
||||
// 'email' => auth()->guard('contact')->user()->email ?: '',
|
||||
// 'address_line1' => auth()->guard('contact')->user()->client->address1 ?: '',
|
||||
// 'city' => auth()->guard('contact')->user()->client->city ?: '',
|
||||
// 'postal_code' => auth()->guard('contact')->user()->client->postal_code ?: '',
|
||||
// 'country_code' => auth()->guard('contact')->user()->client->country->iso_3166_2,
|
||||
// ],
|
||||
// ],
|
||||
// ]);
|
||||
|
||||
// return redirect(
|
||||
// $redirect->redirect_url
|
||||
// );
|
||||
// } catch (\Exception $exception) {
|
||||
// return $this->processUnsuccessfulAuthorization($exception);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +81,7 @@ class DirectDebit implements MethodInterface
|
||||
"params" => [
|
||||
"mandate_request" => [
|
||||
"currency" => auth()->guard('contact')->user()->client->currency()->code,
|
||||
"verify" => "recommended"
|
||||
"verify" => "when_available"
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
@ -404,7 +404,9 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
$this->init();
|
||||
$mandate = $this->gateway->mandates()->get($token);
|
||||
|
||||
if ($mandate->status !== 'active') {
|
||||
if(!in_array($mandate->status, ['pending_submission', 'submitted', 'active','pending_customer_approval'])) {
|
||||
|
||||
// if ($mandate->status !== 'active') {
|
||||
throw new \Exception(ctrans('texts.gocardless_mandate_not_ready'));
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
|
@ -199,7 +199,7 @@ class BaseRepository
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
nlog($model->toArray());
|
||||
$model->saveQuietly();
|
||||
|
||||
/* Model now persisted, now lets do some child tasks */
|
||||
|
@ -12,13 +12,15 @@
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use App\Models\Expense;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
/**
|
||||
* ExpenseRepository.
|
||||
@ -158,4 +160,25 @@ class ExpenseRepository extends BaseRepository
|
||||
|
||||
return $expense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize Expenses in bulk
|
||||
*
|
||||
* @param Collection $expenses
|
||||
* @param int $category_id
|
||||
* @return void
|
||||
*/
|
||||
public function categorize(Collection $expenses, int $category_id): void
|
||||
{
|
||||
$ec = ExpenseCategory::withTrashed()->find($category_id);
|
||||
|
||||
$expenses->when($ec)
|
||||
->each(function ($expense) use($ec){
|
||||
|
||||
$expense->category_id = $ec->id;
|
||||
$expense->save();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -175,15 +175,13 @@ class FacturaEInvoice extends AbstractService
|
||||
->setBillingPeriod()
|
||||
->signDocument();
|
||||
|
||||
$disk = config('filesystems.default');
|
||||
// $disk = config('filesystems.default');
|
||||
|
||||
if (!Storage::disk($disk)->exists($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()))) {
|
||||
Storage::makeDirectory($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()));
|
||||
}
|
||||
// if (!Storage::disk($disk)->exists($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()))) {
|
||||
// Storage::makeDirectory($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()));
|
||||
// }
|
||||
|
||||
// $this->fac->export(Storage::disk($disk)->path($this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xsig")));
|
||||
return $this->fac->export();
|
||||
// return $this->invoice->client->e_invoice_filepath($this->invoice->invitations->first()) . $this->invoice->getFileName("xsig");
|
||||
|
||||
}
|
||||
|
||||
@ -546,8 +544,8 @@ class FacturaEInvoice extends AbstractService
|
||||
"isLegalEntity" => $this->invoice->client->classification === 'individual' ? false : true,
|
||||
"taxNumber" => $this->invoice->client->vat_number,
|
||||
"name" => substr($this->invoice->client->present()->name(), 0, 40),
|
||||
"firstSurname" => substr($this->invoice->client->present()->first_name(), 0, 40),
|
||||
"lastSurname" => substr($this->invoice->client->present()->last_name(), 0, 40),
|
||||
// "firstSurname" => substr($this->invoice->client->present()->last_name(), 0, 40),
|
||||
// "lastSurname" => substr($this->invoice->client->present()->last_name(), 0, 40),
|
||||
"address" => substr($this->invoice->client->address1, 0, 80),
|
||||
"postCode" => substr($this->invoice->client->postal_code, 0, 5),
|
||||
"town" => substr($this->invoice->client->city, 0, 50),
|
||||
@ -563,6 +561,11 @@ class FacturaEInvoice extends AbstractService
|
||||
// "ineTownCode" => "280796" // Cód. de municipio del INE
|
||||
]);
|
||||
|
||||
if($this->invoice->client->classification === 'individual') {
|
||||
$buyer['name'] = $this->invoice->client->present()->first_name();
|
||||
$buyer['firstSurname'] = $this->invoice->client->present()->last_name();
|
||||
}
|
||||
|
||||
$this->fac->setBuyer($buyer);
|
||||
|
||||
return $this;
|
||||
|
@ -442,7 +442,6 @@ class InvoiceService
|
||||
$this->invoice->line_items = array_values($items);
|
||||
|
||||
$this->invoice = $this->invoice->calc()->getInvoice();
|
||||
$this->deleteEInvoice(); //@deprecated
|
||||
|
||||
/* 24-03-2022 */
|
||||
$new_balance = $this->invoice->balance;
|
||||
|
@ -522,7 +522,7 @@ class TemplateService
|
||||
'balance_raw' => ($payment->amount - $payment->refunded - $payment->applied),
|
||||
'date' => $this->translateDate($payment->date, $payment->client->date_format(), $payment->client->locale()),
|
||||
'method' => $payment->translatedType(),
|
||||
'currency' => $payment->currency->code,
|
||||
'currency' => $payment->currency->code ?? $payment->company->currency()->code,
|
||||
'exchange_rate' => $payment->exchange_rate,
|
||||
'transaction_reference' => $payment->transaction_reference,
|
||||
'is_manual' => $payment->is_manual,
|
||||
|
@ -15,11 +15,11 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION','5.7.42'),
|
||||
'app_tag' => env('APP_TAG','5.7.42'),
|
||||
'app_version' => env('APP_VERSION','5.7.43'),
|
||||
'app_tag' => env('APP_TAG','5.7.43'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
'google_maps_api_key' => env('GOOGLE_MAPS_API_KEY'),
|
||||
'google_analytics_url' => env('GOOGLE_ANALYTICS_URL', 'https://www.google-analytics.com/collect'),
|
||||
'key_length' => 32,
|
||||
|
@ -27,7 +27,7 @@ class BankTransactionFactory extends Factory
|
||||
'amount' => $this->faker->randomFloat(2, 10, 10000) ,
|
||||
'currency_id' => '1',
|
||||
'account_type' => 'creditCard',
|
||||
'category_id' => 1,
|
||||
'category_id' => null,
|
||||
'category_type' => 'Random' ,
|
||||
'date' => $this->faker->date('Y-m-d') ,
|
||||
'bank_account_id' => 1 ,
|
||||
|
@ -11,11 +11,12 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use Tests\TestCase;
|
||||
use App\Models\Expense;
|
||||
use Tests\MockAccountData;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
@ -232,6 +233,42 @@ class ExpenseApiTest extends TestCase
|
||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
||||
}
|
||||
|
||||
public function testExpenseBulkCategorize()
|
||||
{
|
||||
|
||||
|
||||
$e = Expense::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
]);
|
||||
|
||||
|
||||
$ec = ExpenseCategory::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'name' => 'Test Category',
|
||||
]);
|
||||
|
||||
nlog("expense category id = {$ec->hashed_id}");
|
||||
|
||||
$data = [
|
||||
'category_id' => $ec->hashed_id,
|
||||
'action' => 'bulk_categorize',
|
||||
'ids' => [$this->encodePrimaryKey($e->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expenses/bulk', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
nlog($arr);
|
||||
|
||||
$this->assertEquals($ec->hashed_id, $arr['data'][0]['category_id']);
|
||||
}
|
||||
|
||||
|
||||
public function testAddingExpense()
|
||||
{
|
||||
$data = [
|
||||
|
@ -195,7 +195,7 @@ class UserTest extends TestCase
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $company_token->token,
|
||||
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
])->get("/api/v1/users?without={$company_token->user->hashed_id}&status=active");
|
||||
])->get("/api/v1/users?status=active&without={$company_token->user->hashed_id}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertCount(0, $response->json()['data']);
|
||||
@ -204,7 +204,7 @@ class UserTest extends TestCase
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $company_token->token,
|
||||
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
])->get("/api/v1/users?without={$company_token->user->hashed_id}&status=archived");
|
||||
])->get("/api/v1/users?status=archived&without={$company_token->user->hashed_id}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertCount(1, $response->json()['data']);
|
||||
@ -213,7 +213,7 @@ class UserTest extends TestCase
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $company_token->token,
|
||||
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
])->get("/api/v1/users?without={$company_token->user->hashed_id}&status=deleted");
|
||||
])->get("/api/v1/users?status=deleted&without={$company_token->user->hashed_id}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertCount(0, $response->json()['data']);
|
||||
|
242
tests/Unit/ArrayFiltersTest.php
Normal file
242
tests/Unit/ArrayFiltersTest.php
Normal file
@ -0,0 +1,242 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\User;
|
||||
use App\Models\Design;
|
||||
use App\Models\Payment;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
class ArrayFiltersTest extends TestCase
|
||||
{
|
||||
private string $import_version = '';
|
||||
|
||||
private array $version_keys = [
|
||||
'baseline' =>[],
|
||||
'5.7.34' => [
|
||||
Payment::class => [
|
||||
'is_deleted',
|
||||
'amount',
|
||||
]
|
||||
],
|
||||
'5.7.35' => [
|
||||
Payment::class => [
|
||||
'date',
|
||||
'transaction_reference',
|
||||
],
|
||||
User::class => [
|
||||
'user_logged_in_notification',
|
||||
'first_name',
|
||||
'last_name',
|
||||
],
|
||||
Design::class => [
|
||||
'is_template',
|
||||
]
|
||||
],
|
||||
'5.7.36' => [
|
||||
Payment::class => [
|
||||
'type_id',
|
||||
'status_id',
|
||||
],
|
||||
],
|
||||
'5.7.37' => [
|
||||
Payment::class => [
|
||||
'currency_id',
|
||||
'hashed_id',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
protected function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPaymentFilterFactory()
|
||||
{
|
||||
$p = Payment::factory()->make()->toArray();
|
||||
|
||||
$this->assertIsArray($p);
|
||||
}
|
||||
|
||||
public function testPaymentUnsetProps()
|
||||
{
|
||||
$p = Payment::factory()->make()->toArray();
|
||||
|
||||
$version = '5.7.36';
|
||||
$current_version = config('ninja.app_version');
|
||||
|
||||
$this->assertNotEquals($current_version, $version);
|
||||
|
||||
$index = 0;
|
||||
$version_index = 0;
|
||||
|
||||
foreach($this->version_keys as $key => $value)
|
||||
{
|
||||
if($version == $key)
|
||||
{
|
||||
$version_index = $index;
|
||||
}
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
$this->assertEquals(3, $version_index);
|
||||
|
||||
$filters = collect($this->version_keys)->slice($version_index);
|
||||
|
||||
$this->assertEquals(2, $filters->count());
|
||||
|
||||
$x = collect($p)->diffKeys($filters->flatten()->flip());
|
||||
|
||||
$this->assertEquals(4, $x->count());
|
||||
}
|
||||
|
||||
public function testPaymentUnsetPropsScenario2()
|
||||
{
|
||||
$p = Payment::factory()->make()->toArray();
|
||||
|
||||
$version = '5.7.35';
|
||||
$current_version = config('ninja.app_version');
|
||||
|
||||
$this->assertNotEquals($current_version, $version);
|
||||
|
||||
$index = 0;
|
||||
$version_index = 0;
|
||||
|
||||
foreach($this->version_keys as $key => $value)
|
||||
{
|
||||
if($version == $key)
|
||||
{
|
||||
$version_index = $index;
|
||||
}
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
$this->assertEquals(2, $version_index);
|
||||
|
||||
$index = 0;
|
||||
$version_index = 0;
|
||||
|
||||
$filters = collect($this->version_keys)
|
||||
->map(function ($value, $key) use ($version, &$version_index, &$index) {
|
||||
if($version == $key)
|
||||
$version_index = $index;
|
||||
|
||||
$index++;
|
||||
return $value;
|
||||
|
||||
})
|
||||
->slice($version_index)
|
||||
->pluck(Payment::class);
|
||||
|
||||
$this->assertEquals(3, $filters->count());
|
||||
|
||||
$x = collect($p)->diffKeys($filters->flatten()->flip());
|
||||
|
||||
$this->assertEquals(2, $x->count());
|
||||
}
|
||||
|
||||
public function testWhenScenario()
|
||||
{
|
||||
$p = Payment::factory()->make()->toArray();
|
||||
|
||||
$version = '5.7.35';
|
||||
$current_version = '5.7.35';
|
||||
|
||||
$filters = collect($this->version_keys)
|
||||
->map(function ($value, $key) use ($version, &$version_index, &$index) {
|
||||
if($version == $key)
|
||||
$version_index = $index;
|
||||
|
||||
$index++;
|
||||
return $value;
|
||||
|
||||
})
|
||||
->slice($version_index)
|
||||
->pluck(Payment::class);
|
||||
|
||||
$this->assertEquals(3, $filters->count());
|
||||
}
|
||||
|
||||
public function testWhenScenario2()
|
||||
{
|
||||
$p = Payment::factory()->make()->toArray();
|
||||
|
||||
$version = '5.7.33';
|
||||
$current_version = '5.7.35';
|
||||
|
||||
$filters = collect($this->version_keys)
|
||||
->map(function ($value, $key) use ($version, &$version_index, &$index) {
|
||||
if($version == $key) {
|
||||
$version_index = $index;
|
||||
nlog("version = {$version_index}");
|
||||
}
|
||||
$index++;
|
||||
return $value;
|
||||
|
||||
})
|
||||
->slice($version_index ?? 0)
|
||||
->pluck(Payment::class);
|
||||
|
||||
$x = collect($p)->diffKeys($filters->filter()->flatten()->flip());
|
||||
|
||||
$this->assertEquals(5, $filters->count());
|
||||
}
|
||||
|
||||
|
||||
private function filterArray($class, array $obj_array)
|
||||
{
|
||||
$index = 0;
|
||||
$version_index = 0;
|
||||
|
||||
$filters = collect($this->version_keys)
|
||||
->map(function ($value, $key) use (&$version_index, &$index) {
|
||||
if($this->import_version == $key) {
|
||||
$version_index = $index;
|
||||
}
|
||||
|
||||
$index++;
|
||||
return $value;
|
||||
|
||||
})
|
||||
->when($version_index == 0, function ($collection){
|
||||
return collect([]);
|
||||
})
|
||||
->when($version_index > 0, function ($collection) use (&$version_index, $class) {
|
||||
return $collection->slice($version_index)->pluck($class)->filter();
|
||||
});
|
||||
|
||||
return collect($obj_array)->diffKeys($filters->flatten()->flip())->toArray();
|
||||
|
||||
// return $filters->count() > 0 ? collect($obj_array)->diffKeys($filters->flatten()->flip())->toArray() : $obj_array;
|
||||
|
||||
}
|
||||
|
||||
public function testFilterArrayOne()
|
||||
{
|
||||
$u = User::factory()->make()->toArray();
|
||||
|
||||
$prop_count = count($u);
|
||||
|
||||
$this->import_version = '5.7.42';
|
||||
|
||||
$filtered_u = $this->filterArray(User::class, $u);
|
||||
|
||||
$this->assertCount($prop_count, $filtered_u);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user