mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge branch 'bank_rules' into v5-develop
This commit is contained in:
commit
7d589c5a7c
30
app/Factory/BankTransactionRuleFactory.php
Normal file
30
app/Factory/BankTransactionRuleFactory.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\BankTransactionRule;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BankTransactionRuleFactory
|
||||
{
|
||||
public static function create(int $company_id, int $user_id) :BankTransactionRule
|
||||
{
|
||||
$bank_transaction_rule = new BankTransactionRule;
|
||||
$bank_transaction_rule->user_id = $user_id;
|
||||
$bank_transaction_rule->company_id = $company_id;
|
||||
|
||||
$bank_transaction_rule->name = '';
|
||||
$bank_transaction_rule->rules = [];
|
||||
|
||||
return $bank_transaction_rule;
|
||||
}
|
||||
}
|
133
app/Filters/BankTransactionRuleFilters.php
Normal file
133
app/Filters/BankTransactionRuleFilters.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/**
|
||||
* BankTransactionRuleilters.
|
||||
*/
|
||||
class BankTransactionRuleFilters extends QueryFilters
|
||||
{
|
||||
/**
|
||||
* Filter by name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Builder
|
||||
*/
|
||||
public function name(string $name = ''): Builder
|
||||
{
|
||||
if(strlen($name) >=1)
|
||||
return $this->builder->where('name', 'like', '%'.$name.'%');
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter based on search text.
|
||||
*
|
||||
* @param string query filter
|
||||
* @return Builder
|
||||
* @deprecated
|
||||
*/
|
||||
public function filter(string $filter = '') : Builder
|
||||
{
|
||||
if (strlen($filter) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
return $this->builder->where(function ($query) use ($filter) {
|
||||
$query->where('bank_transaction_rules.name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the list based on the status
|
||||
* archived, active, deleted.
|
||||
*
|
||||
* @param string filter
|
||||
* @return Builder
|
||||
*/
|
||||
public function status(string $filter = '') : Builder
|
||||
{
|
||||
if (strlen($filter) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$table = 'bank_transaction_rules';
|
||||
$filters = explode(',', $filter);
|
||||
|
||||
return $this->builder->where(function ($query) use ($filters, $table) {
|
||||
$query->whereNull($table.'.id');
|
||||
|
||||
if (in_array(parent::STATUS_ACTIVE, $filters)) {
|
||||
$query->orWhereNull($table.'.deleted_at');
|
||||
}
|
||||
|
||||
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
|
||||
$query->orWhere(function ($query) use ($table) {
|
||||
$query->whereNotNull($table.'.deleted_at');
|
||||
|
||||
if (! in_array($table, ['users'])) {
|
||||
$query->where($table.'.is_deleted', '=', 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array(parent::STATUS_DELETED, $filters)) {
|
||||
$query->orWhere($table.'.is_deleted', '=', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the list based on $sort.
|
||||
*
|
||||
* @param string sort formatted as column|asc
|
||||
* @return Builder
|
||||
*/
|
||||
public function sort(string $sort) : Builder
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base query.
|
||||
*
|
||||
* @param int company_id
|
||||
* @param User $user
|
||||
* @return Builder
|
||||
* @deprecated
|
||||
*/
|
||||
public function baseQuery(int $company_id, User $user) : Builder
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the query by the users company ID.
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function entityFilter()
|
||||
{
|
||||
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
|
||||
return $this->builder->company();
|
||||
}
|
||||
}
|
505
app/Http/Controllers/BankTransactionRuleController.php
Normal file
505
app/Http/Controllers/BankTransactionRuleController.php
Normal file
@ -0,0 +1,505 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\BankTransactionFactory;
|
||||
use App\Factory\BankTransactionRuleFactory;
|
||||
use App\Filters\BankTransactionFilters;
|
||||
use App\Filters\BankTransactionRuleFilters;
|
||||
use App\Helpers\Bank\Yodlee\Yodlee;
|
||||
use App\Http\Requests\BankTransactionRule\CreateBankTransactionRuleRequest;
|
||||
use App\Http\Requests\BankTransactionRule\DestroyBankTransactionRuleRequest;
|
||||
use App\Http\Requests\BankTransactionRule\EditBankTransactionRuleRequest;
|
||||
use App\Http\Requests\BankTransactionRule\ShowBankTransactionRuleRequest;
|
||||
use App\Http\Requests\BankTransactionRule\StoreBankTransactionRuleRequest;
|
||||
use App\Http\Requests\BankTransactionRule\UpdateBankTransactionRuleRequest;
|
||||
use App\Http\Requests\BankTransaction\AdminBankTransactionRuleRequest;
|
||||
use App\Http\Requests\Import\PreImportRequest;
|
||||
use App\Jobs\Bank\MatchBankTransactionRules;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Repositories\BankTransactionRepository;
|
||||
use App\Repositories\BankTransactionRuleRepository;
|
||||
use App\Services\Bank\BankMatchingService;
|
||||
use App\Transformers\BankTransactionRuleTransformer;
|
||||
use App\Transformers\BankTransactionTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BankTransactionRuleController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
protected $entity_type = BankTransactionRule::class;
|
||||
|
||||
protected $entity_transformer = BankTransactionRuleTransformer::class;
|
||||
|
||||
protected $bank_transaction_repo;
|
||||
|
||||
public function __construct(BankTransactionRuleRepository $bank_transaction_repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->bank_transaction_repo = $bank_transaction_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/bank_transaction_rules",
|
||||
* operationId="getBankTransactionRules",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Gets a list of bank_transaction_rules",
|
||||
* description="Lists all bank transaction rules",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||
* @OA\Parameter(
|
||||
* name="rows",
|
||||
* in="query",
|
||||
* description="The number of bank integrations to return",
|
||||
* example="50",
|
||||
* required=false,
|
||||
* @OA\Schema(
|
||||
* type="number",
|
||||
* format="integer",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="A list of bank integrations",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
* @param BankTransactionFilters $filter
|
||||
* @return Response|mixed
|
||||
*/
|
||||
public function index(BankTransactionRuleFilters $filters)
|
||||
{
|
||||
|
||||
$bank_transaction_rules = BankTransactionRule::filter($filters);
|
||||
|
||||
return $this->listResponse($bank_transaction_rules);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param ShowBankTransactionRuleRequest $request
|
||||
* @param BankTransactionRule $bank_transaction_rule
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/bank_transaction_rules/{id}",
|
||||
* operationId="showBankTransactionRule",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Shows a bank_transaction",
|
||||
* description="Displays a bank_transaction by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Bank Transaction RuleHashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the bank_transaction rule object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function show(ShowBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
return $this->itemResponse($bank_transaction_rule);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param EditBankTransactionRuleRequest $request
|
||||
* @param BankTransactionRule $bank_transaction_rule
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/bank_transaction_rules/{id}/edit",
|
||||
* operationId="editBankTransactionRule",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Shows a bank_transaction for editing",
|
||||
* description="Displays a bank_transaction by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Bank Transaction Rule Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the bank_transaction rule object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function edit(EditBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
return $this->itemResponse($bank_transaction_rule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UpdateBankTransactionRuleRequest $request
|
||||
* @param BankTransactionRule $bank_transaction_rule
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/bank_transaction_rules/{id}",
|
||||
* operationId="updateBankTransactionRule",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Updates a bank_transaction Rule",
|
||||
* description="Handles the updating of a bank_transaction rule by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Bank Transaction Rule Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the bank_transaction rule object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function update(UpdateBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
|
||||
//stubs for updating the model
|
||||
$bank_transaction = $this->bank_transaction_repo->save($request->all(), $bank_transaction_rule);
|
||||
|
||||
return $this->itemResponse($bank_transaction_rule->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param CreateBankTransactionRuleRequest $request
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/bank_transaction_rules/create",
|
||||
* operationId="getBankTransactionRulesCreate",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Gets a new blank bank_transaction rule object",
|
||||
* description="Returns a blank object with default values",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="A blank bank_transaction rule object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function create(CreateBankTransactionRuleRequest $request)
|
||||
{
|
||||
$bank_transaction_rule = BankTransactionRuleFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id);
|
||||
|
||||
return $this->itemResponse($bank_transaction_rule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param StoreBankTransactionRuleRequest $request
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/bank_transaction_rules",
|
||||
* operationId="storeBankTransaction",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Adds a bank_transaction rule",
|
||||
* description="Adds an bank_transaction to a company",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the saved bank_transaction rule object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function store(StoreBankTransactionRuleRequest $request)
|
||||
{
|
||||
//stub to store the model
|
||||
$bank_transaction_rule = $this->bank_transaction_repo->save($request->all(), BankTransactionRuleFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id));
|
||||
|
||||
return $this->itemResponse($bank_transaction_rule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param DestroyBankTransactionRuleRequest $request
|
||||
* @param BankTransactionRule $bank_transaction_rule
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @throws \Exception
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/bank_transaction_rules/{id}",
|
||||
* operationId="deleteBankTransactionRule",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Deletes a bank_transaction rule",
|
||||
* description="Handles the deletion of a bank_transaction rule by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Bank Transaction Rule Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns a HTTP status",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function destroy(DestroyBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
$this->bank_transaction_repo->delete($bank_transaction_rule);
|
||||
|
||||
return $this->itemResponse($bank_transaction_rule->fresh());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view.
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/bank_transation_rules/bulk",
|
||||
* operationId="bulkBankTransactionRules",
|
||||
* tags={"bank_transaction_rules"},
|
||||
* summary="Performs bulk actions on an array of bank_transation rules",
|
||||
* description="",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||
* @OA\RequestBody(
|
||||
* description="Action paramters",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="integer",
|
||||
* description="Array of hashed IDs to be bulk 'actioned",
|
||||
* example="[0,1,2,3]",
|
||||
* ),
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The Bulk Action response",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
|
||||
if(!in_array($action, ['archive', 'restore', 'delete']))
|
||||
return response()->json(['message' => 'Unsupported action.'], 400);
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$bank_transaction_rules = BankTransactionRule::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
|
||||
|
||||
$bank_transaction_rules->each(function ($bank_transaction_rule, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $bank_transaction_rule)) {
|
||||
$this->bank_transaction_repo->{$action}($bank_transaction_rule);
|
||||
}
|
||||
});
|
||||
|
||||
/* Need to understand which permission are required for the given bulk action ie. view / edit */
|
||||
|
||||
return $this->listResponse(BankTransactionRule::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
|
||||
}
|
||||
|
||||
}
|
@ -108,6 +108,7 @@ class BaseController extends Controller
|
||||
'company.system_logs',
|
||||
'company.bank_integrations',
|
||||
'company.bank_transactions',
|
||||
'company.bank_transaction_rules',
|
||||
];
|
||||
|
||||
private $mini_load = [
|
||||
@ -126,6 +127,7 @@ class BaseController extends Controller
|
||||
'company.expense_categories',
|
||||
'company.subscriptions',
|
||||
'company.bank_integrations',
|
||||
'company.bank_transaction_rules',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
@ -456,6 +458,13 @@ class BaseController extends Controller
|
||||
$query->where('bank_transactions.user_id', $user->id);
|
||||
}
|
||||
},
|
||||
'company.bank_transaction_rules'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if (! $user->isAdmin()) {
|
||||
$query->where('bank_transaction_rules.user_id', $user->id);
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@ -530,6 +539,12 @@ class BaseController extends Controller
|
||||
$query->where('bank_integrations.user_id', $user->id);
|
||||
}
|
||||
},
|
||||
'company.bank_transaction_rules'=> function ($query) use ($updated_at, $user) {
|
||||
|
||||
if (! $user->isAdmin()) {
|
||||
$query->where('bank_transaction_rules.user_id', $user->id);
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
|
10
app/Http/Controllers/OpenAPI/BTRulesSchema.php
Normal file
10
app/Http/Controllers/OpenAPI/BTRulesSchema.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="BTRules",
|
||||
* type="object",
|
||||
* @OA\Property(property="data_key", type="string", example="description,amount", description="The key to search"),
|
||||
* @OA\Property(property="operator", type="string", example=">", description="The operator flag of the search"),
|
||||
* @OA\Property(property="value", type="string" ,example="bob", description="The value to search for"),
|
||||
* )
|
||||
*/
|
@ -6,7 +6,7 @@
|
||||
* @OA\Property(property="id", type="string", example="AS3df3A", description="The bank integration hashed id"),
|
||||
* @OA\Property(property="company_id", type="string", example="AS3df3A", description="The company hashed id"),
|
||||
* @OA\Property(property="user_id", type="string", example="AS3df3A", description="The user hashed id"),
|
||||
* @OA\Property(property="transaction_id", type="integer", example=343434, description="The id of the transaction"),
|
||||
* @OA\Property(property="transaction_id", type="integer", example=343434, description="The id of the transaction rule"),
|
||||
* @OA\Property(property="amount", type="number", example=10.00, description="The transaction amount"),
|
||||
* @OA\Property(property="currency_id", type="string", example="1", description="The currency ID of the currency"),
|
||||
* @OA\Property(property="account_type", type="string", example="creditCard", description="The account type"),
|
||||
|
25
app/Http/Controllers/OpenAPI/BankTransactionRule.php
Normal file
25
app/Http/Controllers/OpenAPI/BankTransactionRule.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="BankTransactionRule",
|
||||
* type="object",
|
||||
* @OA\Property(property="id", type="string", example="AS3df3A", description="The bank transaction rules hashed id"),
|
||||
* @OA\Property(property="company_id", type="string", example="AS3df3A", description="The company hashed id"),
|
||||
* @OA\Property(property="user_id", type="string", example="AS3df3A", description="The user hashed id"),
|
||||
* @OA\Property(property="name", type="string", example="Rule 1", description="The name of the transaction"),
|
||||
* @OA\Property(
|
||||
* property="rules",
|
||||
* type="array",
|
||||
* description="A mapped collection of the sub rules for the BankTransactionRule",
|
||||
* @OA\Items(
|
||||
* ref="#/components/schemas/BTRules",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Property(property="auto_convert", type="boolean", example=true, description="Flags whether the rule converts the transaction automatically"),
|
||||
* @OA\Property(property="matches_on_all", type="boolean", example=true, description="Flags whether all subrules are required for the match"),
|
||||
* @OA\Property(property="applies_to", type="string", example="CREDIT", description="Flags whether the rule applies to a CREDIT or DEBIT"),
|
||||
* @OA\Property(property="client_id", type="string", example="AS3df3A", description="The client hashed id"),
|
||||
* @OA\Property(property="vendor_id", type="string", example="AS3df3A", description="The vendor hashed id"),
|
||||
* @OA\Property(property="category_id", type="string", example="AS3df3A", description="The category hashed id"),
|
||||
* )
|
||||
*/
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\BankTransactionRule;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\BankTransactionRule;
|
||||
|
||||
class CreateBankTransactionRuleRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', BankTransactionRule::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\BankTransactionRule;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class DestroyBankTransactionRuleRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->bank_transaction_rule);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\BankTransactionRule;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class EditBankTransactionRuleRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->bank_transaction_rule);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\BankTransactionRule;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class ShowBankTransactionRuleRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('view', $this->bank_transaction_rule);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\BankTransactionRule;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class StoreBankTransactionRuleRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', BankTransactionRule::class);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
$rules = [
|
||||
'name' => 'bail|required|string',
|
||||
'rules' => 'bail|array',
|
||||
'rules.*.operator' => 'bail|required|nullable',
|
||||
'rules.*.search_key' => 'bail|required|nullable',
|
||||
'rules.*.value' => 'bail|required|nullable',
|
||||
'auto_convert' => 'bail|sometimes|bool',
|
||||
'matches_on_all' => 'bail|sometimes|bool',
|
||||
'applies_to' => 'bail|sometimes|string',
|
||||
];
|
||||
|
||||
if(isset($this->category_id))
|
||||
$rules['category_id'] = 'bail|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
if(isset($this->vendor_id))
|
||||
$rules['vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
if(isset($this->client_id))
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\BankTransactionRule;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class UpdateBankTransactionRuleRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->bank_transaction_rule);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
$rules = [
|
||||
'name' => 'bail|required|string',
|
||||
'rules' => 'bail|array',
|
||||
'rules.*.operator' => 'bail|required|nullable',
|
||||
'rules.*.search_key' => 'bail|required|nullable',
|
||||
'rules.*.value' => 'bail|required|nullable',
|
||||
'auto_convert' => 'bail|sometimes|bool',
|
||||
'matches_on_all' => 'bail|sometimes|bool',
|
||||
'applies_to' => 'bail|sometimes|string',
|
||||
];
|
||||
|
||||
if(isset($this->category_id))
|
||||
$rules['category_id'] = 'bail|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
if(isset($this->vendor_id))
|
||||
$rules['vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
if(isset($this->client_id))
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
}
|
@ -125,6 +125,10 @@ class Request extends FormRequest
|
||||
$input['company_gateway_id'] = $this->decodePrimaryKey($input['company_gateway_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (isset($input['client_contacts'])) {
|
||||
foreach ($input['client_contacts'] as $key => $contact) {
|
||||
if (! array_key_exists('send_email', $contact) || ! array_key_exists('id', $contact)) {
|
||||
|
@ -46,6 +46,7 @@ use App\Repositories\PaymentRepository;
|
||||
use App\Repositories\ProductRepository;
|
||||
use App\Repositories\QuoteRepository;
|
||||
use App\Repositories\VendorRepository;
|
||||
use App\Services\Bank\BankMatchingService;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
@ -107,6 +108,8 @@ class Csv extends BaseImport implements ImportInterface
|
||||
$bank_transaction_count = $this->ingest($data, $entity_type);
|
||||
$this->entity_count['bank_transactions'] = $bank_transaction_count;
|
||||
|
||||
BankMatchingService::dispatchSync($this->company->id, $this->company->db);
|
||||
|
||||
}
|
||||
|
||||
public function client()
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Filterable;
|
||||
use App\Models\Invoice;
|
||||
use App\Services\Bank\BankService;
|
||||
|
171
app/Models/BankTransactionRule.php
Normal file
171
app/Models/BankTransactionRule.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Filterable;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class BankTransactionRule extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
use MakesHash;
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'rules',
|
||||
'auto_convert',
|
||||
'matches_on_all',
|
||||
'applies_to',
|
||||
'client_id',
|
||||
'vendor_id',
|
||||
'category_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'rules' => 'array',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
];
|
||||
|
||||
protected $dates = [
|
||||
];
|
||||
|
||||
protected array $search_keys = [
|
||||
'description' => 'string',
|
||||
'amount' => 'number',
|
||||
];
|
||||
|
||||
/* Amount */
|
||||
protected array $number_operators = [
|
||||
'=',
|
||||
'>',
|
||||
'>=',
|
||||
'<',
|
||||
'<='
|
||||
];
|
||||
|
||||
/* Description, Client, Vendor, Reference Number */
|
||||
protected array $string_operators = [
|
||||
'is',
|
||||
'contains',
|
||||
'starts_with',
|
||||
'is_empty',
|
||||
];
|
||||
|
||||
private array $search_results = [];
|
||||
|
||||
// rule object looks like this:
|
||||
//[
|
||||
// {
|
||||
// 'search_key': 'client_id',
|
||||
// 'operator' : 'is',
|
||||
// 'value' : 'Sparky'
|
||||
// }
|
||||
//]
|
||||
|
||||
// public function processRule(BankTransaction $bank_transaction)
|
||||
// {
|
||||
// foreach($this->rules as $key => $rule)
|
||||
// {
|
||||
// $this->search($rule, $key, $bank_transaction);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private function search($rule, $key, $bank_transaction)
|
||||
// {
|
||||
// if($rule->search_key == 'amount')
|
||||
// {
|
||||
// //number search
|
||||
// }
|
||||
// else {
|
||||
// //string search
|
||||
// }
|
||||
// }
|
||||
|
||||
// private function findAmount($amount, $bank_transaction)
|
||||
// {
|
||||
// if($bank_transaction->base_type == 'CREDIT'){
|
||||
// //search invoices
|
||||
// }
|
||||
// else{
|
||||
// //search expenses
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// private function searchClient($rule, $bank_transaction)
|
||||
// {
|
||||
// if($bank_transaction->base_type == 'CREDIT'){
|
||||
// //search invoices
|
||||
// }
|
||||
// else{
|
||||
// //search expenses
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// private function searchVendor($rule, $bank_transaction)
|
||||
// {
|
||||
// //search expenses
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// private function searchDescription($rule, $bank_transaction)
|
||||
// {
|
||||
// //search expenses public notes
|
||||
// }
|
||||
|
||||
// private function searchReference($rule, $bank_transaction)
|
||||
// {
|
||||
// if($bank_transaction->base_type == 'CREDIT'){
|
||||
// //search invoices
|
||||
// }
|
||||
// else{
|
||||
// //search expenses
|
||||
// }
|
||||
// }
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function vendor()
|
||||
{
|
||||
return $this->belongsTo(Vendor::class);
|
||||
}
|
||||
|
||||
public function client()
|
||||
{
|
||||
return $this->belongsTo(Client::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function expense_cateogry()
|
||||
{
|
||||
return $this->belongsTo(ExpenseCategory::class)->withTrashed();
|
||||
}
|
||||
|
||||
}
|
@ -13,6 +13,7 @@ namespace App\Models;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Language;
|
||||
use App\Models\Presenters\CompanyPresenter;
|
||||
use App\Models\PurchaseOrder;
|
||||
@ -189,6 +190,11 @@ class Company extends BaseModel
|
||||
return $this->hasMany(BankTransaction::class);
|
||||
}
|
||||
|
||||
public function bank_transaction_rules()
|
||||
{
|
||||
return $this->hasMany(BankTransactionRule::class);
|
||||
}
|
||||
|
||||
public function getCompanyIdAttribute()
|
||||
{
|
||||
return $this->encodePrimaryKey($this->id);
|
||||
@ -542,6 +548,23 @@ class Company extends BaseModel
|
||||
return $this->company_users()->withTrashed()->where('is_owner', true)->first()?->user;
|
||||
}
|
||||
|
||||
public function credit_rules()
|
||||
{
|
||||
return BankTransactionRule::query()
|
||||
->where('company_id', $this->id)
|
||||
->where('applies_to', 'CREDIT')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function debit_rules()
|
||||
{
|
||||
return BankTransactionRule::query()
|
||||
->where('company_id', $this->id)
|
||||
->where('applies_to', 'DEBIT')
|
||||
->get();
|
||||
}
|
||||
|
||||
|
||||
public function resolveRouteBinding($value, $field = null)
|
||||
{
|
||||
return $this->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
||||
|
31
app/Policies/BankTransactionRulePolicy.php
Normal file
31
app/Policies/BankTransactionRulePolicy.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* Class BankTransactionPolicy.
|
||||
*/
|
||||
class BankTransactionRulePolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* Checks if the user has create permissions.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user) : bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use App\Models\Activity;
|
||||
use App\Models\Bank;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
@ -45,6 +46,7 @@ use App\Models\Webhook;
|
||||
use App\Policies\ActivityPolicy;
|
||||
use App\Policies\BankIntegrationPolicy;
|
||||
use App\Policies\BankTransactionPolicy;
|
||||
use App\Policies\BankTransactionRulePolicy;
|
||||
use App\Policies\ClientPolicy;
|
||||
use App\Policies\CompanyGatewayPolicy;
|
||||
use App\Policies\CompanyPolicy;
|
||||
@ -86,6 +88,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
Activity::class => ActivityPolicy::class,
|
||||
BankIntegration::class => BankIntegrationPolicy::class,
|
||||
BankTransaction::class => BankTransactionPolicy::class,
|
||||
BankTransactionRule::class => BankTransactionRulePolicy::class,
|
||||
Client::class => ClientPolicy::class,
|
||||
Company::class => CompanyPolicy::class,
|
||||
CompanyToken::class => CompanyTokenPolicy::class,
|
||||
|
@ -28,17 +28,11 @@ class BankTransactionRepository extends BaseRepository
|
||||
$bank_transaction->bank_integration_id = $data['bank_integration_id'];
|
||||
|
||||
$bank_transaction->fill($data);
|
||||
|
||||
$bank_transaction->save();
|
||||
|
||||
if($bank_transaction->base_type == 'CREDIT' && $invoice = $bank_transaction->service()->matchInvoiceNumber())
|
||||
{
|
||||
$bank_transaction->invoice_ids = $invoice->hashed_id;
|
||||
$bank_transaction->status_id = BankTransaction::STATUS_MATCHED;
|
||||
$bank_transaction->save();
|
||||
}
|
||||
$bank_transaction->service()->processRules();
|
||||
|
||||
return $bank_transaction;
|
||||
return $bank_transaction->fresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
35
app/Repositories/BankTransactionRuleRepository.php
Normal file
35
app/Repositories/BankTransactionRuleRepository.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
|
||||
/**
|
||||
* Class for bank transaction rule repository.
|
||||
*/
|
||||
class BankTransactionRuleRepository extends BaseRepository
|
||||
{
|
||||
|
||||
public function save($data, BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
|
||||
$bank_transaction_rule->fill($data);
|
||||
|
||||
$bank_transaction_rule->save();
|
||||
|
||||
return $bank_transaction_rule;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -11,35 +11,35 @@
|
||||
|
||||
namespace App\Services\Bank;
|
||||
|
||||
use App\Factory\ExpenseCategoryFactory;
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\Company;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\Invoice;
|
||||
use App\Services\Bank\BankService;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class BankMatchingService implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private $company_id;
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesCounter;
|
||||
|
||||
private Company $company;
|
||||
|
||||
private $db;
|
||||
|
||||
private $invoices;
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public function __construct($company_id, $db)
|
||||
{
|
||||
$this->company_id = $company_id;
|
||||
$this->db = $db;
|
||||
}
|
||||
public function __construct(private int $company_id, private string $db){}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
@ -48,37 +48,27 @@ class BankMatchingService implements ShouldQueue
|
||||
|
||||
$this->company = Company::find($this->company_id);
|
||||
|
||||
$this->invoices = Invoice::where('company_id', $this->company->id)
|
||||
->whereIn('status_id', [1,2,3])
|
||||
->where('is_deleted', 0)
|
||||
->get();
|
||||
$this->matchTransactions();
|
||||
|
||||
|
||||
$this->match();
|
||||
}
|
||||
|
||||
private function match()
|
||||
private function matchTransactions()
|
||||
{
|
||||
|
||||
|
||||
BankTransaction::where('company_id', $this->company->id)
|
||||
->where('status_id', BankTransaction::STATUS_UNMATCHED)
|
||||
->cursor()
|
||||
->each(function ($bt){
|
||||
|
||||
$invoice = $this->invoices->first(function ($value, $key) use ($bt){
|
||||
->where('status_id', BankTransaction::STATUS_UNMATCHED)
|
||||
->cursor()
|
||||
->each(function ($bt){
|
||||
|
||||
(new BankService($bt))->processRules();
|
||||
|
||||
return str_contains($bt->description, $value->number);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
if($invoice)
|
||||
{
|
||||
$bt->invoice_ids = $invoice->hashed_id;
|
||||
$bt->status_id = BankTransaction::STATUS_MATCHED;
|
||||
$bt->save();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function middleware()
|
||||
{
|
||||
return [new WithoutOverlapping($this->company_id)];
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace App\Services\Bank;
|
||||
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\Invoice;
|
||||
use App\Services\Bank\ProcessBankRule;
|
||||
use App\Services\Bank\ProcessBankRules;
|
||||
|
||||
class BankService
|
||||
{
|
||||
@ -40,11 +40,9 @@ class BankService
|
||||
|
||||
}
|
||||
|
||||
public function processRule($rule)
|
||||
public function processRules()
|
||||
{
|
||||
(new ProcessBankRule($this->bank_transaction, $rule))->run();
|
||||
|
||||
return $this;
|
||||
(new ProcessBankRules($this->bank_transaction))->run();
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Bank;
|
||||
|
||||
use App\Models\BankTransaction;
|
||||
use App\Services\AbstractService;
|
||||
|
||||
class ProcessBankRule extends AbstractService
|
||||
{
|
||||
|
||||
public function __construct(private BankTransaction $bank_transaction, $rule){}
|
||||
|
||||
public function run() : void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
206
app/Services/Bank/ProcessBankRules.php
Normal file
206
app/Services/Bank/ProcessBankRules.php
Normal file
@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Bank;
|
||||
|
||||
use App\Factory\ExpenseCategoryFactory;
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\Invoice;
|
||||
use App\Services\AbstractService;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ProcessBankRules extends AbstractService
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
protected $credit_rules;
|
||||
|
||||
protected $debit_rules;
|
||||
|
||||
protected $categories;
|
||||
|
||||
public function __construct(public BankTransaction $bank_transaction){}
|
||||
|
||||
public function run()
|
||||
{
|
||||
if($this->bank_transaction->base_type == 'DEBIT')
|
||||
$this->matchDebit();
|
||||
else
|
||||
$this->matchCredit();
|
||||
}
|
||||
|
||||
private function matchCredit()
|
||||
{
|
||||
|
||||
$this->credit_rules = $this->bank_transaction->company->credit_rules();
|
||||
|
||||
$this->invoices = Invoice::where('company_id', $this->bank_transaction->company_id)
|
||||
->whereIn('status_id', [1,2,3])
|
||||
->where('is_deleted', 0)
|
||||
->get();
|
||||
|
||||
$invoice = $this->invoices->first(function ($value, $key){
|
||||
|
||||
return str_contains($this->bank_transaction->description, $value->number);
|
||||
|
||||
});
|
||||
|
||||
if($invoice)
|
||||
{
|
||||
$this->bank_transaction->invoice_ids = $invoice->hashed_id;
|
||||
$this->bank_transaction->status_id = BankTransaction::STATUS_MATCHED;
|
||||
$this->bank_transaction->save();
|
||||
return;
|
||||
}
|
||||
|
||||
//stub for credit rules
|
||||
foreach($this->credit_rules as $rule)
|
||||
{
|
||||
// $this->bank_transaction->bank_rule_id = $bank_transaction_rule->id;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function matchDebit()
|
||||
{
|
||||
|
||||
$this->debit_rules = $this->bank_transaction->company->debit_rules();
|
||||
|
||||
$this->categories = collect(Cache::get('bank_categories'));
|
||||
|
||||
foreach($this->debit_rules as $bank_transaction_rule)
|
||||
{
|
||||
|
||||
$matches = 0;
|
||||
|
||||
foreach($bank_transaction_rule['rules'] as $rule)
|
||||
{
|
||||
$rule_count = count($bank_transaction_rule['rules']);
|
||||
|
||||
if($rule['search_key'] == 'description')
|
||||
{
|
||||
|
||||
if($this->matchStringOperator($this->bank_transaction->description, $rule['value'], $rule['operator'])){
|
||||
$matches++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($rule['search_key'] == 'amount')
|
||||
{
|
||||
|
||||
if($this->matchNumberOperator($this->bank_transaction->amount, $rule['value'] , $rule['operator'])){
|
||||
$matches++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(($bank_transaction_rule['matches_on_all'] && ($matches == $rule_count)) || (!$bank_transaction_rule['matches_on_all'] && $matches > 0))
|
||||
{
|
||||
|
||||
// $this->bank_transaction->client_id = empty($rule['client_id']) ? null : $rule['client_id'];
|
||||
$this->bank_transaction->vendor_id = $bank_transaction_rule->vendor_id;
|
||||
$this->bank_transaction->ninja_category_id = $bank_transaction_rule->category_id;
|
||||
$this->bank_transaction->status_id = BankTransaction::STATUS_MATCHED;
|
||||
$this->bank_transaction->bank_rule_id = $bank_transaction_rule->id;
|
||||
$this->bank_transaction->save();
|
||||
|
||||
if($bank_transaction_rule['auto_convert'])
|
||||
{
|
||||
|
||||
$expense = ExpenseFactory::create($this->bank_transaction->company_id, $this->bank_transaction->user_id);
|
||||
$expense->category_id = $bank_transaction_rule->category_id ?: $this->resolveCategory();
|
||||
$expense->amount = $this->bank_transaction->amount;
|
||||
$expense->number = $this->getNextExpenseNumber($expense);
|
||||
$expense->currency_id = $this->bank_transaction->currency_id;
|
||||
$expense->date = Carbon::parse($this->bank_transaction->date);
|
||||
$expense->payment_date = Carbon::parse($this->bank_transaction->date);
|
||||
$expense->transaction_reference = $this->bank_transaction->description;
|
||||
$expense->transaction_id = $this->bank_transaction->id;
|
||||
$expense->vendor_id = $bank_transaction_rule->vendor_id;
|
||||
$expense->invoice_documents = $this->bank_transaction->company->invoice_expense_documents;
|
||||
$expense->should_be_invoiced = $this->bank_transaction->company->mark_expenses_invoiceable;
|
||||
$expense->save();
|
||||
|
||||
$this->bank_transaction->expense_id = $expense->id;
|
||||
$this->bank_transaction->status_id = BankTransaction::STATUS_CONVERTED;
|
||||
$this->bank_transaction->save();
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function resolveCategory()
|
||||
{
|
||||
$category = $this->categories->firstWhere('highLevelCategoryId', $this->bank_transaction->category_id);
|
||||
|
||||
$ec = ExpenseCategory::where('company_id', $this->bank_transaction->company_id)->where('bank_category_id', $this->bank_transaction->category_id)->first();
|
||||
|
||||
if($ec)
|
||||
return $ec->id;
|
||||
|
||||
if($category)
|
||||
{
|
||||
$ec = ExpenseCategoryFactory::create($this->bank_transaction->company_id, $this->bank_transaction->user_id);
|
||||
$ec->bank_category_id = $this->bank_transaction->category_id;
|
||||
$ec->name = $category->highLevelCategoryName;
|
||||
$ec->save();
|
||||
|
||||
return $ec->id;
|
||||
}
|
||||
}
|
||||
|
||||
private function matchNumberOperator($bt_value, $rule_value, $operator) :bool
|
||||
{
|
||||
|
||||
return match ($operator) {
|
||||
'>' => floatval($bt_value) > floatval($rule_value),
|
||||
'>=' => floatval($bt_value) >= floatval($rule_value),
|
||||
'=' => floatval($bt_value) == floatval($rule_value),
|
||||
'<' => floatval($bt_value) < floatval($rule_value),
|
||||
'<=' => floatval($bt_value) <= floatval($rule_value),
|
||||
default => false,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private function matchStringOperator($bt_value, $rule_value, $operator) :bool
|
||||
{
|
||||
$bt_value = strtolower(str_replace(" ", "", $bt_value));
|
||||
$rule_value = strtolower(str_replace(" ", "", $rule_value));
|
||||
$rule_length = iconv_strlen($rule_value);
|
||||
|
||||
return match ($operator) {
|
||||
'is' => $bt_value == $rule_value,
|
||||
'contains' => stripos($bt_value, $rule_value) !== false,
|
||||
'starts_with' => substr($bt_value, 0, $rule_length) == $rule_value,
|
||||
'is_empty' => empty($bt_value),
|
||||
default => false,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
90
app/Transformers/BankTransactionRuleTransformer.php
Normal file
90
app/Transformers/BankTransactionRuleTransformer.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Transformers;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Company;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Transformers\VendorTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
/**
|
||||
* Class BankTransactionRuleTransformer.
|
||||
*/
|
||||
class BankTransactionRuleTransformer extends EntityTransformer
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultIncludes = [
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $availableIncludes = [
|
||||
'company',
|
||||
'vendor',
|
||||
'client',
|
||||
'expense_category',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param BankTransaction $bank_integration
|
||||
* @return array
|
||||
*/
|
||||
public function transform(BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
return [
|
||||
'id' => (string) $this->encodePrimaryKey($bank_transaction_rule->id),
|
||||
'name' => (string) $bank_transaction_rule->name,
|
||||
'rules' => $bank_transaction_rule->rules ?: (array) [],
|
||||
'auto_convert' => (bool) $bank_transaction_rule->auto_convert,
|
||||
'matches_on_all' => (bool) $bank_transaction_rule->matches_on_all,
|
||||
'applies_to' => (string) $bank_transaction_rule->applies_to,
|
||||
'client_id' => $this->encodePrimaryKey($bank_transaction_rule->client_id) ?: '',
|
||||
'vendor_id' => $this->encodePrimaryKey($bank_transaction_rule->vendor_id) ?: '',
|
||||
'category_id' => $this->encodePrimaryKey($bank_transaction_rule->category_id) ?: '',
|
||||
'is_deleted' => (bool) $bank_transaction_rule->is_deleted,
|
||||
'created_at' => (int) $bank_transaction_rule->created_at,
|
||||
'updated_at' => (int) $bank_transaction_rule->updated_at,
|
||||
'archived_at' => (int) $bank_transaction_rule->deleted_at,
|
||||
];
|
||||
}
|
||||
|
||||
public function includeCompany(BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
$transformer = new CompanyTransformer($this->serializer);
|
||||
|
||||
return $this->includeItem($bank_transaction_rule->company, $transformer, Company::class);
|
||||
}
|
||||
|
||||
public function includeClient(BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
$transformer = new ClientTransformer($this->serializer);
|
||||
|
||||
return $this->includeItem($bank_transaction_rule->expense, $transformer, Client::class);
|
||||
}
|
||||
|
||||
public function includeVendor(BankTransactionRule $bank_transaction_rule)
|
||||
{
|
||||
$transformer = new VendorTransformer($this->serializer);
|
||||
|
||||
return $this->includeItem($bank_transaction_rule->vendor, $transformer, Vendor::class);
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,7 @@ use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Webhook;
|
||||
use App\Transformers\BankIntegrationTransformer;
|
||||
use App\Transformers\BankTransactionRuleTransformer;
|
||||
use App\Transformers\BankTransactionTransformer;
|
||||
use App\Transformers\PurchaseOrderTransformer;
|
||||
use App\Transformers\RecurringExpenseTransformer;
|
||||
@ -104,6 +105,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'purchase_orders',
|
||||
'bank_integrations',
|
||||
'bank_transactions',
|
||||
'bank_transaction_rules',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -232,6 +234,14 @@ class CompanyTransformer extends EntityTransformer
|
||||
return $this->includeCollection($company->bank_transactions, $transformer, BankTransaction::class);
|
||||
}
|
||||
|
||||
|
||||
public function includeBankTransactionRules(Company $company)
|
||||
{
|
||||
$transformer = new BankTransactionRuleTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($company->bank_transaction_rules, $transformer, BankTransactionRule::class);
|
||||
}
|
||||
|
||||
public function includeBankIntegrations(Company $company)
|
||||
{
|
||||
$transformer = new BankIntegrationTransformer($this->serializer);
|
||||
|
31
database/factories/BankTransactionRuleFactory.php
Normal file
31
database/factories/BankTransactionRuleFactory.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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 Database\Factories;
|
||||
|
||||
use App\Models\Account;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BankTransactionRuleFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'name' =>$this->faker->name(),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
Schema::create('bank_transaction_rules', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('company_id');
|
||||
$table->unsignedInteger('user_id');
|
||||
|
||||
$table->string('name'); //name of rule
|
||||
$table->mediumText('rules')->nullable(); //array of rule objects
|
||||
$table->boolean('auto_convert')->default(false); //auto convert to match
|
||||
$table->boolean('matches_on_all')->default(false); //match on all rules or just one
|
||||
$table->string('applies_to')->default('CREDIT'); //CREDIT/DEBIT
|
||||
|
||||
$table->unsignedInteger('client_id')->nullable();
|
||||
$table->unsignedInteger('vendor_id')->nullable();
|
||||
$table->unsignedInteger('category_id')->nullable();
|
||||
|
||||
$table->boolean('is_deleted')->default(0);
|
||||
$table->timestamps(6);
|
||||
$table->softDeletes('deleted_at', 6);
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
|
||||
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -14,9 +14,10 @@ use App\Http\Controllers\AccountController;
|
||||
use App\Http\Controllers\ActivityController;
|
||||
use App\Http\Controllers\Auth\ForgotPasswordController;
|
||||
use App\Http\Controllers\Auth\LoginController;
|
||||
use App\Http\Controllers\Bank\YodleeController;
|
||||
use App\Http\Controllers\BankIntegrationController;
|
||||
use App\Http\Controllers\BankTransactionController;
|
||||
use App\Http\Controllers\BankTransactionRuleController;
|
||||
use App\Http\Controllers\Bank\YodleeController;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Controllers\ChartController;
|
||||
use App\Http\Controllers\ClientController;
|
||||
@ -119,6 +120,9 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
|
||||
Route::post('bank_transactions/bulk', [BankTransactionController::class, 'bulk'])->name('bank_transactions.bulk');
|
||||
Route::post('bank_transactions/match', [BankTransactionController::class, 'match'])->name('bank_transactions.match');
|
||||
|
||||
Route::resource('bank_transaction_rules', BankTransactionRuleController::class); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::post('bank_transaction_rules/bulk', [BankTransactionRuleController::class, 'bulk'])->name('bank_transaction_rules.bulk');
|
||||
|
||||
Route::post('check_subdomain', [SubdomainController::class, 'index'])->name('check_subdomain');
|
||||
Route::get('ping', [PingController::class, 'index'])->name('ping');
|
||||
Route::get('health_check', [PingController::class, 'health'])->name('health_check');
|
||||
|
766
tests/Feature/Bank/BankTransactionRuleTest.php
Normal file
766
tests/Feature/Bank/BankTransactionRuleTest.php
Normal file
@ -0,0 +1,766 @@
|
||||
<?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\Feature\Bank;
|
||||
|
||||
use App\Factory\BankIntegrationFactory;
|
||||
use App\Factory\BankTransactionFactory;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BankTransactionRuleTest extends TestCase
|
||||
{
|
||||
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
protected function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
|
||||
$this->withoutExceptionHandling();
|
||||
}
|
||||
|
||||
public function testValidationContainsRule()
|
||||
{
|
||||
//[{"search_key":"description","operator":"contains","value":"hello"}]
|
||||
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'category_id' =>$this->expense_category->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'contains',
|
||||
'value' => 'hello',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'HellO ThErE CowBoY',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
$this->assertNotNull($bt->expense->category_id);
|
||||
$this->assertNotNull($bt->expense->vendor_id);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testUpdateValidationRules()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'amount',
|
||||
'operator' => '<=',
|
||||
'value' => 100,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
$data = [
|
||||
"applies_to" => "DEBIT",
|
||||
"archived_at" => 0,
|
||||
"auto_convert" => False,
|
||||
"category_id" => $this->expense_category->hashed_id,
|
||||
"is_deleted" => False,
|
||||
"isChanged" => True,
|
||||
"matches_on_all" => True,
|
||||
"name" => "TEST 22",
|
||||
"updated_at" => 1669060432,
|
||||
"vendor_id" => $this->vendor->hashed_id
|
||||
];
|
||||
|
||||
$response = null;
|
||||
|
||||
|
||||
try {
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/bank_transaction_rules/'. $br->hashed_id, $data);
|
||||
|
||||
} catch (ValidationException $e) {
|
||||
$message = json_decode($e->validator->getMessageBag(), 1);
|
||||
nlog($message);
|
||||
}
|
||||
|
||||
if($response){
|
||||
$arr = $response->json();
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testMatchingBankTransactionExpenseAmountLessThanEqualTo()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'amount',
|
||||
'operator' => '<=',
|
||||
'value' => 100,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => '',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseAmountLessThan()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'amount',
|
||||
'operator' => '<',
|
||||
'value' => 100,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => '',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 99
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
public function testMatchingBankTransactionExpenseAmountGreaterThan()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'amount',
|
||||
'operator' => '>',
|
||||
'value' => 100,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => '',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 101
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseAmountMiss()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'amount',
|
||||
'operator' => '=',
|
||||
'value' => 100,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => '',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 101
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNull($bt->expense_id);
|
||||
}
|
||||
|
||||
public function testMatchingBankTransactionExpenseAmount()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'amount',
|
||||
'operator' => '=',
|
||||
'value' => 100,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => '',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseIsEmpty()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'is_empty',
|
||||
'value' => '',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => '',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
public function testMatchingBankTransactionExpenseIsEmptyMiss()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'is_empty',
|
||||
'value' => '',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'asdadsa',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseStartsWithMiss()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'starts_with',
|
||||
'value' => 'chesst',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'ChESSSty coughs are terrible',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseStartsWith()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'starts_with',
|
||||
'value' => 'chess',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'ChESSSty coughs are terrible',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseContainsMiss()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'contains',
|
||||
'value' => 'asdddfd',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'Something asd bizarre',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionExpenseContains()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'contains',
|
||||
'value' => 'asd',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'Something asd bizarre',
|
||||
'base_type' => 'DEBIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
public function testMatchingBankTransactionExpenseMiss()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'is',
|
||||
'value' => 'wallaby',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'Wall',
|
||||
'base_type' => 'DEBIT',
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNull($bt->expense_id);
|
||||
}
|
||||
|
||||
public function testMatchingBankTransactionExpense()
|
||||
{
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'is',
|
||||
'value' => 'wallaby',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'WallABy',
|
||||
'base_type' => 'DEBIT',
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertNotNull($bt->expense_id);
|
||||
}
|
||||
|
||||
|
||||
public function testMatchingBankTransactionInvoice()
|
||||
{
|
||||
|
||||
$this->invoice->number = "MUHMUH";
|
||||
$this->invoice->save();
|
||||
|
||||
$br = BankTransactionRule::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'matches_on_all' => false,
|
||||
'auto_convert' => true,
|
||||
'applies_to' => 'CREDIT',
|
||||
'client_id' => $this->client->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'rules' => [
|
||||
[
|
||||
'search_key' => 'description',
|
||||
'operator' => 'is',
|
||||
'value' => 'MUHMUH',
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$bt = BankTransaction::factory()->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'description' => 'MUHMUH',
|
||||
'base_type' => 'CREDIT',
|
||||
'amount' => 100
|
||||
]);
|
||||
|
||||
|
||||
$bt->service()->processRules();
|
||||
|
||||
$bt = $bt->fresh();
|
||||
|
||||
$this->assertEquals(BankTransaction::STATUS_MATCHED, $bt->status_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
230
tests/Feature/BankTransactionRuleApiTest.php
Normal file
230
tests/Feature/BankTransactionRuleApiTest.php
Normal file
@ -0,0 +1,230 @@
|
||||
<?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\Feature;
|
||||
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\BankTransactionRuleController
|
||||
*/
|
||||
class BankTransactionRuleApiTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
protected function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
/*
|
||||
$rules = [
|
||||
'name' => 'bail|required|string',
|
||||
'rules' => 'bail|array',
|
||||
'auto_convert' => 'bail|sometimes|bool',
|
||||
'matches_on_all' => 'bail|sometimes|bool',
|
||||
'applies_to' => 'bail|sometimes|bool',
|
||||
];
|
||||
|
||||
if(isset($this->category_id))
|
||||
$rules['category_id'] = 'bail|sometimes|exists:expense_categories,id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
if(isset($this->vendor_id))
|
||||
$rules['vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
|
||||
if(isset($this->client_id))
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
*/
|
||||
public function testBankRuleCategoryIdValidation()
|
||||
{
|
||||
$data = [
|
||||
'name' => 'The First Rule',
|
||||
'rules' => [
|
||||
[
|
||||
"operator" => "contains",
|
||||
"search_key" => "description",
|
||||
"value" => "mobile"
|
||||
],
|
||||
],
|
||||
'assigned_user_id' => null,
|
||||
'auto_convert' => false,
|
||||
'matches_on_all' => true,
|
||||
'applies_to' => 'DEBIT',
|
||||
'category_id' => $this->expense_category->hashed_id,
|
||||
'vendor_id' => $this->vendor->hashed_id
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/bank_transaction_rules/', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$this->assertEquals('DEBIT', $arr['data']['applies_to']);
|
||||
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/bank_transaction_rules/'. $arr['data']['id'], $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$this->assertEquals('DEBIT', $arr['data']['applies_to']);
|
||||
}
|
||||
|
||||
public function testBankRulePost()
|
||||
{
|
||||
|
||||
$data = [
|
||||
'name' => 'The First Rule',
|
||||
'rules' => [],
|
||||
'auto_convert' => false,
|
||||
'matches_on_all' => false,
|
||||
'applies_to' => 'CREDIT',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/bank_transaction_rules/', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$this->assertEquals('The First Rule', $arr['data']['name']);
|
||||
|
||||
}
|
||||
|
||||
public function testBankRulePut()
|
||||
{
|
||||
|
||||
$data = [
|
||||
'name' => 'The First Rule',
|
||||
'rules' => [],
|
||||
'auto_convert' => false,
|
||||
'matches_on_all' => false,
|
||||
'applies_to' => 'CREDIT',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson('/api/v1/bank_transaction_rules/', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$this->assertEquals('The First Rule', $arr['data']['name']);
|
||||
|
||||
$data = [
|
||||
'name' => 'A New Name For The First Rule',
|
||||
'rules' => [],
|
||||
'auto_convert' => false,
|
||||
'matches_on_all' => false,
|
||||
'applies_to' => 'CREDIT',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson('/api/v1/bank_transaction_rules/'. $arr['data']['id'], $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$this->assertEquals('A New Name For The First Rule', $arr['data']['name']);
|
||||
|
||||
}
|
||||
|
||||
public function testBankTransactionRuleGet()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/bank_transaction_rules/'.$this->encodePrimaryKey($this->bank_transaction_rule->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testBankTransactionRuleArchived()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->bank_transaction_rule->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/bank_transaction_rules/bulk?action=archive', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotNull($arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testBankTransactionRuleRestored()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->bank_transaction_rule->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/bank_transaction_rules/bulk?action=restore', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testBankTransactionRuleDeleted()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->bank_transaction_rule->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/bank_transaction_rules/bulk?action=delete', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Models\Account;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
@ -153,6 +154,11 @@ trait MockAccountData
|
||||
*/
|
||||
public $bank_transaction;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
public $bank_transaction_rule;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@ -572,6 +578,11 @@ trait MockAccountData
|
||||
'bank_integration_id' => $this->bank_integration->id,
|
||||
]);
|
||||
|
||||
$this->bank_transaction_rule = BankTransactionRule::factory()->create([
|
||||
'user_id' => $user_id,
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
|
||||
$invitations = CreditInvitation::whereCompanyId($this->credit->company_id)
|
||||
->whereCreditId($this->credit->id);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user