diff --git a/VERSION.txt b/VERSION.txt index fec0973fe321..ec9ff787c070 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.118 \ No newline at end of file +5.5.119 \ No newline at end of file diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 2df86c581f9d..0d34120a66f5 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -17,7 +17,6 @@ use App\Utils\Ninja; use Sentry\State\Scope; use Illuminate\Support\Arr; use Illuminate\Http\Request; -use Illuminate\Http\Response; use Sentry\Laravel\Integration; use Illuminate\Support\Facades\Schema; use GuzzleHttp\Exception\ConnectException; @@ -35,20 +34,21 @@ use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException; +use InvalidArgumentException; class Handler extends ExceptionHandler { /** * A list of the exception types that are not reported. * - * @var array + * @var array> */ protected $dontReport = [ - PDOException::class, + // PDOException::class, MaxAttemptsExceededException::class, CommandNotFoundException::class, ValidationException::class, - ModelNotFoundException::class, + // ModelNotFoundException::class, NotFoundHttpException::class, ]; diff --git a/app/Factory/RecurringExpenseToExpenseFactory.php b/app/Factory/RecurringExpenseToExpenseFactory.php index 1fc9626aa72e..98c2a7beee6f 100644 --- a/app/Factory/RecurringExpenseToExpenseFactory.php +++ b/app/Factory/RecurringExpenseToExpenseFactory.php @@ -40,7 +40,7 @@ class RecurringExpenseToExpenseFactory $expense->tax_name3 = $recurring_expense->tax_name3; $expense->tax_rate3 = $recurring_expense->tax_rate3; $expense->date = now()->format('Y-m-d'); - $expense->payment_date = $recurring_expense->payment_date; + $expense->payment_date = $recurring_expense->payment_date ?: now()->format('Y-m-d'); $expense->amount = $recurring_expense->amount; $expense->foreign_amount = $recurring_expense->foreign_amount ?: 0; diff --git a/app/Http/Controllers/ChartController.php b/app/Http/Controllers/ChartController.php index 651ded493fbb..85b387d30202 100644 --- a/app/Http/Controllers/ChartController.php +++ b/app/Http/Controllers/ChartController.php @@ -22,45 +22,6 @@ class ChartController extends BaseController } /** - * @OA\Post( - * path="/api/v1/charts/totals", - * operationId="getChartTotals", - * tags={"charts"}, - * summary="Get chart data", - * description="Get chart data", - * @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 activities to return", - * example="50", - * required=false, - * @OA\Schema( - * type="number", - * format="integer", - * ), - * ), - * @OA\Response( - * response=200, - * description="json dataset of chart data", - * @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"), - * ), - * ) * @param ShowChartRequest $request */ public function totals(ShowChartRequest $request) @@ -81,4 +42,28 @@ class ChartController extends BaseController return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200); } + + /** + * @param ShowChartRequest $request + */ + public function totalsV2(ShowChartRequest $request) + { + /** @var \App\Models\User auth()->user() */ + $user = auth()->user(); + $cs = new ChartService($user->company(), $user, $user->isAdmin()); + + return response()->json($cs->totals($request->input('start_date'), $request->input('end_date')), 200); + } + + public function chart_summaryV2(ShowChartRequest $request) + { + + /** @var \App\Models\User auth()->user() */ + $user = auth()->user(); + $cs = new ChartService($user->company(), $user, $user->isAdmin()); + + return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200); + } + + } diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 20263da9d34b..7997de5101d0 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -145,8 +145,11 @@ class DocumentController extends BaseController * @return Response */ public function update(UpdateDocumentRequest $request, Document $document) - { - return $this->itemResponse($document); + { + $document->fill($request->all()); + $document->save(); + + return $this->itemResponse($document->fresh()); } /** diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php index 195065f62f21..24e19e955991 100644 --- a/app/Http/Controllers/TaskSchedulerController.php +++ b/app/Http/Controllers/TaskSchedulerController.php @@ -37,28 +37,6 @@ class TaskSchedulerController extends BaseController parent::__construct(); } - /** - * @OA\GET( - * path="/api/v1/task_schedulers/", - * operationId="getTaskSchedulers", - * tags={"task_schedulers"}, - * summary="Task Scheduler Index", - * description="Get all schedulers with associated jobs", - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Response( - * response=200, - * description="success", - * @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="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - */ public function index(SchedulerFilters $filters) { $schedulers = Scheduler::filter($filters); @@ -66,44 +44,6 @@ class TaskSchedulerController extends BaseController return $this->listResponse($schedulers); } - /** - * Show the form for creating a new resource. - * - * @param CreateSchedulerRequest $request The request - * - * @return Response - * - * - * @OA\Get( - * path="/api/v1/invoices/task_schedulers", - * operationId="getTaskScheduler", - * tags={"task_schedulers"}, - * summary="Gets a new blank scheduler object", - * description="Returns a blank object with default values", - * @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 scheduler 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/TaskSchedulerSchema"), - * ), - * @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(CreateSchedulerRequest $request) { $scheduler = SchedulerFactory::create(auth()->user()->company()->id, auth()->user()->id); @@ -111,39 +51,6 @@ class TaskSchedulerController extends BaseController return $this->itemResponse($scheduler); } - /** - * @OA\Post( - * path="/api/v1/task_schedulers/", - * operationId="createTaskScheduler", - * tags={"task_schedulers"}, - * summary="Create task scheduler with job ", - * description="Create task scheduler with a job (action(job) request should be sent via request also. Example: We want client report to be job which will be run - * multiple times, we should send the same parameters in the request as we would send if we wanted to get report, see example", - * @OA\Parameter(ref="#/components/parameters/X-API-SECRET"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\RequestBody( - * required=true, - * @OA\JsonContent(ref="#/components/schemas/TaskSchedulerSchema") - * ), - * @OA\Response( - * response=200, - * description="success", - * @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 store(StoreSchedulerRequest $request) { $scheduler = $this->scheduler_repository->save($request->all(), SchedulerFactory::create(auth()->user()->company()->id, auth()->user()->id)); @@ -151,86 +58,12 @@ class TaskSchedulerController extends BaseController return $this->itemResponse($scheduler); } - /** - * @OA\GET( - * path="/api/v1/task_schedulers/{id}", - * operationId="showTaskScheduler", - * tags={"task_schedulers"}, - * summary="Show given scheduler", - * description="Get scheduler with associated job", - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Scheduler Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="success", - * @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="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - */ + public function show(ShowSchedulerRequest $request, Scheduler $scheduler) { return $this->itemResponse($scheduler); } - /** - * @OA\PUT( - * path="/api/v1/task_schedulers/{id}", - * operationId="updateTaskScheduler", - * tags={"task_schedulers"}, - * summary="Update task scheduler ", - * description="Update task scheduler", - * @OA\Parameter(ref="#/components/parameters/X-API-SECRET"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Scheduler Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), * @OA\RequestBody( - * required=true, - * @OA\JsonContent(ref="#/components/schemas/TaskSchedulerSchema") - * ), - * @OA\Response( - * response=200, - * description="success", - * @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 update(UpdateSchedulerRequest $request, Scheduler $scheduler) { $this->scheduler_repository->save($request->all(), $scheduler); @@ -238,39 +71,6 @@ class TaskSchedulerController extends BaseController return $this->itemResponse($scheduler); } - /** - * @OA\DELETE( - * path="/api/v1/task_schedulers/{id}", - * operationId="destroyTaskScheduler", - * tags={"task_schedulers"}, - * summary="Destroy Task Scheduler", - * description="Destroy task scheduler and its associated job", - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter( - * name="id", - * in="path", - * description="The Scheduler Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="success", - * @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="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - */ public function destroy(DestroySchedulerRequest $request, Scheduler $scheduler) { $this->scheduler_repository->delete($scheduler); @@ -278,57 +78,6 @@ class TaskSchedulerController extends BaseController return $this->itemResponse($scheduler->fresh()); } - - /** - * Perform bulk actions on the list view. - * - * @return Response - * - * - * @OA\Post( - * path="/api/v1/task_schedulers/bulk", - * operationId="bulkTaskSchedulerActions", - * tags={"task_schedulers"}, - * summary="Performs bulk actions on an array of task_schedulers", - * description="", - * @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="array of ids", - * 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 TaskSchedule 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\JsonContent(ref="#/components/schemas/TaskSchedulerSchema"), - * ), - * @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'); diff --git a/app/Http/Controllers/TaskStatusController.php b/app/Http/Controllers/TaskStatusController.php index 188312faa512..01f6756a3970 100644 --- a/app/Http/Controllers/TaskStatusController.php +++ b/app/Http/Controllers/TaskStatusController.php @@ -1,4 +1,13 @@ task_status_repo = $task_status_repo; } - + /** - * @OA\Get( - * path="/api/v1/task_statuses", - * operationId="getTaskStatuses", - * tags={"task_status"}, - * summary="Gets a list of task statuses", - * description="Lists task statuses", - * @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\Response( - * response=200, - * description="A list of task statuses", - * @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/TaskStatus"), - * ), - * @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"), - * ), - * ) + * index + * + * @param TaskStatusFilters $filters + * @return Response */ public function index(TaskStatusFilters $filters) { @@ -82,44 +63,12 @@ class TaskStatusController extends BaseController return $this->listResponse($task_status); } + /** - * Show the form for creating a new resource. - * - * @param CreateTaskStatusRequest $request The request + * create * + * @param CreateTaskStatusRequest $request * @return Response - * - * - * - * @OA\Get( - * path="/api/v1/task_statuses/create", - * operationId="getTaskStatussCreate", - * tags={"task_status"}, - * summary="Gets a new blank TaskStatus object", - * description="Returns a blank object with default values", - * @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 TaskStatus 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/TaskStatus"), - * ), - * @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(CreateTaskStatusRequest $request) { @@ -132,49 +81,11 @@ class TaskStatusController extends BaseController * Store a newly created resource in storage. * * @param StoreTaskStatusRequest $request The request - * * @return Response * - * - * - * @OA\Post( - * path="/api/v1/task_statuses", - * operationId="storeTaskStatus", - * tags={"task_status"}, - * summary="Adds a TaskStatus", - * description="Adds a TaskStatusto the system", - * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\RequestBody( - * description="The task_status request", - * required=true, - * @OA\JsonContent(ref="#/components/schemas/TaskStatus"), - * ), - * @OA\Response( - * response=200, - * description="Returns the saved TaskStatus 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/TaskStatus"), - * ), - * @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(StoreTaskStatusRequest $request) { - // nlog($request->all()); $task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); $task_status->fill($request->all()); @@ -185,46 +96,6 @@ class TaskStatusController extends BaseController } /** - * @OA\Get( - * path="/api/v1/task_statuses/{id}", - * operationId="showTaskStatus", - * tags={"task_status"}, - * summary="Shows a TaskStatus Term", - * description="Displays an TaskStatusby id", - * @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 TaskStatusHashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the TaskStatusobject", - * @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/TaskStatus"), - * ), - * @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 ShowTaskStatusRequest $request * @param TaskStatus $task_status * @return Response|mixed @@ -235,46 +106,6 @@ class TaskStatusController extends BaseController } /** - * @OA\Get( - * path="/api/v1/task_statuses/{id}/edit", - * operationId="editTaskStatuss", - * tags={"task_status"}, - * summary="Shows an TaskStatusfor editting", - * description="Displays an TaskStatusby id", - * @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 TaskStatusHashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the TaskStatus 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/TaskStatus"), - * ), - * @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 EditTaskStatusRequest $request * @param TaskStatus $payment * @return Response|mixed @@ -289,56 +120,18 @@ class TaskStatusController extends BaseController * * @param UpdateTaskStatusRequest $request The request * @param TaskStatus $task_status The payment term - * * @return Response - * - * - * @OA\Put( - * path="/api/v1/task_statuses/{id}", - * operationId="updateTaskStatus", - * tags={"task_status"}, - * summary="Updates a TaskStatus Term", - * description="Handles the updating of an TaskStatus Termby id", - * @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 TaskStatusHashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the TaskStatusobject", - * @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/TaskStatus"), - * ), - * @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(UpdateTaskStatusRequest $request, TaskStatus $task_status) { + $task_status->fill($request->all()); + $reorder = $task_status->isDirty('status_order'); $task_status->save(); + if ($reorder) + $this->task_status_repo->reorder($task_status); + return $this->itemResponse($task_status->fresh()); } @@ -347,50 +140,9 @@ class TaskStatusController extends BaseController * * @param DestroyTaskStatusRequest $request * @param TaskStatus $task_status - * - * @return Response - * - * + * @return Response + * * @throws \Exception - * @OA\Delete( - * path="/api/v1/task_statuses/{id}", - * operationId="deleteTaskStatus", - * tags={"task_statuss"}, - * summary="Deletes a TaskStatus Term", - * description="Handles the deletion of an TaskStatus by id", - * @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 TaskStatusHashed 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(DestroyTaskStatusRequest $request, TaskStatus $task_status) { @@ -401,54 +153,8 @@ class TaskStatusController extends BaseController /** * Perform bulk actions on the list view. - * - * @return Collection - * - * - * @OA\Post( - * path="/api/v1/task_statuses/bulk", - * operationId="bulkTaskStatuss", - * tags={"task_status"}, - * summary="Performs bulk actions on an array of task statuses", - * description="", - * @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="TaskStatus Ter,s", - * 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 TaskStatus Terms 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\JsonContent(ref="#/components/schemas/TaskStatus"), - * ), - * @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 ActionTaskStatusRequest $request + * @return Response */ public function bulk(ActionTaskStatusRequest $request) { diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php index 4b72c48ee8bf..d4ce94df41e7 100644 --- a/app/Http/Requests/Company/UpdateCompanyRequest.php +++ b/app/Http/Requests/Company/UpdateCompanyRequest.php @@ -63,7 +63,7 @@ class UpdateCompanyRequest extends Request if (Ninja::isHosted()) { $rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain($this->all())]; } else { - $rules['subdomain'] = 'nullable|alpha_num'; + $rules['subdomain'] = 'nullable|regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/'; } } diff --git a/app/Http/Requests/Document/UpdateDocumentRequest.php b/app/Http/Requests/Document/UpdateDocumentRequest.php index ff93a634cb37..9fc24354dfa0 100644 --- a/app/Http/Requests/Document/UpdateDocumentRequest.php +++ b/app/Http/Requests/Document/UpdateDocumentRequest.php @@ -30,7 +30,9 @@ class UpdateDocumentRequest extends Request public function rules() { - return []; + return [ + 'name' => 'sometimes|alpha_num' + ]; } public function prepareForValidation() diff --git a/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php b/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php index f613c7881a55..724576ef5b16 100644 --- a/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php +++ b/app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php @@ -60,7 +60,5 @@ class StoreSchedulerRequest extends Request $this->merge(['next_run_client' => $input['next_run']]); } - $this->replace($input); - } } diff --git a/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php b/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php index 48c52a3e3e0f..2480758e9520 100644 --- a/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php +++ b/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php @@ -57,6 +57,5 @@ class UpdateSchedulerRequest extends Request $this->merge(['next_run_client' => $input['next_run']]); } - $this->replace($input); } } diff --git a/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php b/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php index 2861bb1a9551..e7a0b757c9ef 100644 --- a/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php +++ b/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php @@ -33,11 +33,6 @@ class UpdateTaskStatusRequest extends Request { $rules = []; - // 26/10/2021 we disable this as it prevent updating existing task status meta data where the same name already exists - // if ($this->input('name')) { - // $rules['name'] = Rule::unique('task_statuses')->where('company_id', auth()->user()->company()->id)->ignore($this->task_status->id); - // } - return $rules; } diff --git a/app/Jobs/Cron/RecurringExpensesCron.php b/app/Jobs/Cron/RecurringExpensesCron.php index 476f57e7ba81..796d38a3c49d 100644 --- a/app/Jobs/Cron/RecurringExpensesCron.php +++ b/app/Jobs/Cron/RecurringExpensesCron.php @@ -98,15 +98,10 @@ class RecurringExpensesCron } } - private function getRecurringExpenses() - { - //extracting this back to the if/else block to test duplicate crons - } - private function generateExpense(RecurringExpense $recurring_expense) { $expense = RecurringExpenseToExpenseFactory::create($recurring_expense); - $expense->save(); + $expense->saveQuietly(); $expense->number = $this->getNextExpenseNumber($expense); $expense->save(); diff --git a/app/Jobs/Invoice/CheckGatewayFee.php b/app/Jobs/Invoice/CheckGatewayFee.php index e888f38489aa..32ee5ad30a10 100644 --- a/app/Jobs/Invoice/CheckGatewayFee.php +++ b/app/Jobs/Invoice/CheckGatewayFee.php @@ -11,13 +11,14 @@ namespace App\Jobs\Invoice; -use App\Libraries\MultiDB; use App\Models\Invoice; +use App\Libraries\MultiDB; use Illuminate\Bus\Queueable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\Middleware\WithoutOverlapping; class CheckGatewayFee implements ShouldQueue { @@ -40,6 +41,8 @@ class CheckGatewayFee implements ShouldQueue */ public function handle() { + nlog("Checking Gateway Fees for Invoice Id = {$this->invoice_id}"); + MultiDB::setDb($this->db); $i = Invoice::withTrashed()->find($this->invoice_id); @@ -52,4 +55,9 @@ class CheckGatewayFee implements ShouldQueue $i->service()->removeUnpaidGatewayFees(); } } + + public function middleware() + { + return [(new WithoutOverlapping($this->invoice_id.$this->db))]; + } } diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php index 437cc7233fe9..3b8611bb3f1e 100644 --- a/app/Jobs/Mail/NinjaMailerJob.php +++ b/app/Jobs/Mail/NinjaMailerJob.php @@ -72,7 +72,9 @@ class NinjaMailerJob implements ShouldQueue public function backoff() { - return [5, 10, 30, 240]; + // return [5, 10, 30, 240]; + return [rand(5, 10), rand(30, 40), rand(60, 79), rand(160, 400)]; + } public function handle() diff --git a/app/Jobs/Util/WebhookSingle.php b/app/Jobs/Util/WebhookSingle.php index 8e83e018a9f0..d840d8544359 100644 --- a/app/Jobs/Util/WebhookSingle.php +++ b/app/Jobs/Util/WebhookSingle.php @@ -63,7 +63,9 @@ class WebhookSingle implements ShouldQueue public function backoff() { - return [15, 35, 65, 185, 3605]; + // return [15, 35, 65, 185, 3605]; + return [rand(10, 15), rand(30, 40), rand(60, 79), rand(160, 200), rand(3000, 5000)]; + } /** @@ -170,9 +172,9 @@ class WebhookSingle implements ShouldQueue } if ($e->getResponse()->getStatusCode() >= 500) { - nlog("endpoint returned a 500, failing"); + nlog("{$subscription->target_url} returned a 500, failing"); - $message = "The was a problem when connecting to {$subscription->target_url} => status code ". $e->getResponse()->getStatusCode(). " no retry attempted."; + $message = "There was a problem when connecting to {$subscription->target_url} => status code ". $e->getResponse()->getStatusCode(). " no retry attempted."; (new SystemLogger( ['message' => $message], diff --git a/app/Listeners/User/UpdateUserLastLogin.php b/app/Listeners/User/UpdateUserLastLogin.php index 4882cfa26554..9fc383db207f 100644 --- a/app/Listeners/User/UpdateUserLastLogin.php +++ b/app/Listeners/User/UpdateUserLastLogin.php @@ -11,16 +11,17 @@ namespace App\Listeners\User; +use App\Models\SystemLog; +use App\Libraries\MultiDB; +use App\Jobs\Util\SystemLogger; +use App\Mail\User\UserLoggedIn; use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerObject; -use App\Jobs\Util\SystemLogger; -use App\Libraries\MultiDB; -use App\Mail\User\UserLoggedIn; -use App\Models\SystemLog; -use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Support\Facades\Cache; +use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Events\Dispatchable; -use Illuminate\Queue\SerializesModels; +use Illuminate\Broadcasting\InteractsWithSockets; class UpdateUserLastLogin implements ShouldQueue { @@ -51,8 +52,10 @@ class UpdateUserLastLogin implements ShouldQueue $event_vars = $event->event_vars; $ip = array_key_exists('ip', $event->event_vars) ? $event->event_vars['ip'] : 'IP address not resolved'; + $key = "user_logged_in_{$user->id}{$event->company->db}"; - if ($user->ip != $ip) { + + if ($user->ip != $ip && is_null(Cache::get($key))) { $nmo = new NinjaMailerObject; $nmo->mailable = new UserLoggedIn($user, $user->account->companies->first(), $ip); $nmo->company = $user->account->companies->first(); @@ -63,7 +66,8 @@ class UpdateUserLastLogin implements ShouldQueue $user->ip = $ip; $user->save(); } - + + Cache::put($key, true, 60 * 24); $arr = json_encode(['ip' => $ip]); SystemLogger::dispatch( diff --git a/app/Models/Client.php b/app/Models/Client.php index fa7e6f577f0d..18890a81dab9 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -250,6 +250,7 @@ class Client extends BaseModel implements HasLocalePreference 'phone', 'number', 'routing_id', + 'is_tax_exempt', ]; protected $with = [ diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php index 09c403781a6e..c9451b76cb8b 100644 --- a/app/Models/Scheduler.php +++ b/app/Models/Scheduler.php @@ -153,6 +153,9 @@ class Scheduler extends BaseModel $offset = $this->company->timezone_offset(); switch ($this->frequency_id) { + case 0: //used only for email entities + $next_run = now()->startOfDay(); + break; case RecurringInvoice::FREQUENCY_DAILY: $next_run = now()->startOfDay()->addDay(); break; diff --git a/app/Models/TaskStatus.php b/app/Models/TaskStatus.php index 8e30f5fa43ac..3b23281de1c0 100644 --- a/app/Models/TaskStatus.php +++ b/app/Models/TaskStatus.php @@ -63,7 +63,7 @@ class TaskStatus extends BaseModel public $timestamps = true; /** - * @var array + * @var array */ protected $fillable = [ 'name', diff --git a/app/Repositories/SchedulerRepository.php b/app/Repositories/SchedulerRepository.php index f067984b1027..2f2f314cb5c4 100644 --- a/app/Repositories/SchedulerRepository.php +++ b/app/Repositories/SchedulerRepository.php @@ -25,10 +25,14 @@ class SchedulerRepository extends BaseRepository */ public function save(array $data, Scheduler $scheduler): Scheduler { + $scheduler->fill($data); $scheduler->save(); - return $scheduler; + /** 18-5-2023 set client specific send times. */ + $scheduler->calculateNextRun(); + + return $scheduler->fresh(); } } diff --git a/app/Repositories/TaskStatusRepository.php b/app/Repositories/TaskStatusRepository.php index 8692ed51fd35..0ff8904d0c79 100644 --- a/app/Repositories/TaskStatusRepository.php +++ b/app/Repositories/TaskStatusRepository.php @@ -53,4 +53,18 @@ class TaskStatusRepository extends BaseRepository return $task_status; } + + public function reorder(TaskStatus $task_status) + { + + TaskStatus::query() + ->where('company_id', $task_status->company_id) + ->orderByRaw('ISNULL(status_order), status_order ASC') + ->orderBy('updated_at', 'DESC') + ->cursor() + ->each(function ($task_status, $index) { + $task_status->update(['status_order' => $index+1]); + }); + + } } diff --git a/app/Services/Chart/ChartQueriesLegacy.php b/app/Services/Chart/ChartQueriesLegacy.php new file mode 100644 index 000000000000..2e03bca4abee --- /dev/null +++ b/app/Services/Chart/ChartQueriesLegacy.php @@ -0,0 +1,169 @@ + $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date]); + } + + public function getExpenseChartQuery($start_date, $end_date, $currency_id) + { + return DB::select(DB::raw(' + SELECT + sum(expenses.amount) as total, + expenses.date, + IFNULL(expenses.currency_id, :company_currency) AS currency_id + FROM expenses + WHERE (expenses.date BETWEEN :start_date AND :end_date) + AND expenses.company_id = :company_id + AND expenses.is_deleted = 0 + GROUP BY expenses.date + HAVING currency_id = :currency_id + '), [ + 'company_currency' => $this->company->settings->currency_id, + 'currency_id' => $currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } + + /** + * Payments + */ + public function getPaymentQuery($start_date, $end_date) + { + return DB::select(DB::raw(' + SELECT sum(payments.amount) as amount, + IFNULL(payments.currency_id, :company_currency) as currency_id + FROM payments + WHERE payments.is_deleted = 0 + AND payments.company_id = :company_id + AND (payments.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + '), [ + 'company_currency' => $this->company->settings->currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } + + public function getPaymentChartQuery($start_date, $end_date, $currency_id) + { + return DB::select(DB::raw(' + SELECT + sum(payments.amount - payments.refunded) as total, + payments.date, + IFNULL(payments.currency_id, :company_currency) AS currency_id + FROM payments + WHERE payments.status_id IN (4,5,6) + AND (payments.date BETWEEN :start_date AND :end_date) + AND payments.company_id = :company_id + AND payments.is_deleted = 0 + GROUP BY payments.date + HAVING currency_id = :currency_id + '), [ + 'company_currency' => $this->company->settings->currency_id, + 'currency_id' => $currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } + + /** + * Invoices + */ + public function getOutstandingQuery($start_date, $end_date) + { + return DB::select(DB::raw(" + SELECT + sum(invoices.balance) as amount, + IFNULL(JSON_EXTRACT( settings, '$.currency_id' ), :company_currency) AS currency_id + FROM clients + JOIN invoices + on invoices.client_id = clients.id + WHERE invoices.status_id IN (2,3) + AND invoices.company_id = :company_id + AND invoices.balance > 0 + AND clients.is_deleted = 0 + AND invoices.is_deleted = 0 + AND (invoices.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date]); + } + + public function getRevenueQuery($start_date, $end_date) + { + return DB::select(DB::raw(" + SELECT + sum(invoices.paid_to_date) as paid_to_date, + IFNULL(JSON_EXTRACT( settings, '$.currency_id' ), :company_currency) AS currency_id + FROM clients + JOIN invoices + on invoices.client_id = clients.id + WHERE invoices.status_id IN (3,4) + AND invoices.company_id = :company_id + AND invoices.amount > 0 + AND clients.is_deleted = 0 + AND invoices.is_deleted = 0 + AND (invoices.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date]); + } + + public function getInvoiceChartQuery($start_date, $end_date, $currency_id) + { + return DB::select(DB::raw(" + SELECT + sum(invoices.amount) as total, + invoices.date, + IFNULL(CAST(JSON_EXTRACT( settings, '$.currency_id' ) AS SIGNED), :company_currency) AS currency_id + FROM clients + JOIN invoices + on invoices.client_id = clients.id + WHERE invoices.status_id IN (2,3,4) + AND (invoices.date BETWEEN :start_date AND :end_date) + AND invoices.company_id = :company_id + AND clients.is_deleted = 0 + AND invoices.is_deleted = 0 + GROUP BY invoices.date + HAVING currency_id = :currency_id + "), [ + 'company_currency' => (int) $this->company->settings->currency_id, + 'currency_id' => $currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } +} diff --git a/app/Services/Chart/ChartServiceLegacy.php b/app/Services/Chart/ChartServiceLegacy.php new file mode 100644 index 000000000000..84e53cec935c --- /dev/null +++ b/app/Services/Chart/ChartServiceLegacy.php @@ -0,0 +1,162 @@ +company = $company; + } + + /** + * Returns an array of currencies that have + * transacted with a company + */ + public function getCurrencyCodes() :array + { + /* Get all the distinct client currencies */ + $currencies = Client::withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted', 0) + ->distinct() + ->pluck('settings->currency_id as id'); + + /* Push the company currency on also */ + $currencies->push((int) $this->company->settings->currency_id); + + /* Add our expense currencies*/ + $expense_currencies = Expense::withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted', 0) + ->distinct() + ->pluck('currency_id as id'); + + /* Merge and filter by unique */ + $currencies = $currencies->merge($expense_currencies)->unique(); + + $cache_currencies = Cache::get('currencies'); + + $filtered_currencies = $cache_currencies->whereIn('id', $currencies)->all(); + + $final_currencies = []; + + foreach ($filtered_currencies as $c_currency) { + $final_currencies[$c_currency['id']] = $c_currency['code']; + } + + return $final_currencies; + } + + /* Chart Data */ + public function chart_summary($start_date, $end_date) :array + { + $currencies = $this->getCurrencyCodes(); + + $data = []; + + foreach ($currencies as $key => $value) { + $data[$key]['invoices'] = $this->getInvoiceChartQuery($start_date, $end_date, $key); + $data[$key]['payments'] = $this->getPaymentChartQuery($start_date, $end_date, $key); + $data[$key]['expenses'] = $this->getExpenseChartQuery($start_date, $end_date, $key); + } + + return $data; + } + + /* Chart Data */ + + /* Totals */ + + public function totals($start_date, $end_date) :array + { + $data = []; + + $data['currencies'] = $this->getCurrencyCodes(); + + foreach ($data['currencies'] as $key => $value) { + $revenue = $this->getRevenue($start_date, $end_date); + $outstanding = $this->getOutstanding($start_date, $end_date); + $expenses = $this->getExpenses($start_date, $end_date); + + $data[$key]['revenue'] = count($revenue) > 0 ? $revenue[array_search($key, array_column($revenue, 'currency_id'))] : new \stdClass; + $data[$key]['outstanding'] = count($outstanding) > 0 ? $outstanding[array_search($key, array_column($outstanding, 'currency_id'))] : new \stdClass; + $data[$key]['expenses'] = count($expenses) > 0 ? $expenses[array_search($key, array_column($expenses, 'currency_id'))] : new \stdClass; + } + + return $data; + } + + public function getRevenue($start_date, $end_date) :array + { + $revenue = $this->getRevenueQuery($start_date, $end_date); + $revenue = $this->addCurrencyCodes($revenue); + + return $revenue; + } + + public function getOutstanding($start_date, $end_date) :array + { + $outstanding = $this->getOutstandingQuery($start_date, $end_date); + $outstanding = $this->addCurrencyCodes($outstanding); + + return $outstanding; + } + + public function getExpenses($start_date, $end_date) :array + { + $expenses = $this->getExpenseQuery($start_date, $end_date); + $expenses = $this->addCurrencyCodes($expenses); + + return $expenses; + } + + /* Totals */ + + /* Helpers */ + + private function addCurrencyCodes($data_set) :array + { + $currencies = Cache::get('currencies'); + + foreach ($data_set as $key => $value) { + $data_set[$key]->currency_id = str_replace('"', '', $value->currency_id); + $data_set[$key]->code = $this->getCode($currencies, $data_set[$key]->currency_id); + } + + return $data_set; + } + + private function getCode($currencies, $currency_id) :string + { + $currency_id = str_replace('"', '', $currency_id); + + $currency = $currencies->filter(function ($item) use ($currency_id) { + return $item->id == $currency_id; + })->first(); + + if ($currency) { + return $currency->code; + } + + return ''; + } +} diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index ddbfee25fb92..ac606a0b2e9e 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -75,7 +75,8 @@ class Email implements ShouldQueue */ public function backoff() { - return [10, 30, 60, 240]; + // return [10, 30, 60, 240]; + return [rand(10, 20), rand(30, 45), rand(60, 79), rand(160, 400)]; } public function handle() diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php index 0ca327ce1572..51c58ce1c532 100644 --- a/app/Services/PdfMaker/Design.php +++ b/app/Services/PdfMaker/Design.php @@ -26,13 +26,13 @@ class Design extends BaseDesign { use MakesInvoiceValues, DesignHelpers, MakesDates; - /** @var App\Models\Invoice || @var App\Models\Quote */ + /** @var \App\Models\Invoice | \App\Models\Quote | \App\Models\Credit | \App\Models\PurchaseOrder | \App\Models\RecurringInvoice */ public $entity; - /** @var App\Models\Client */ + /** @var \App\Models\Client */ public $client; - /** @var App\Models\Vendor */ + /** @var \App\Models\Vendor */ public $vendor; /** Global state of the design, @var array */ diff --git a/app/Services/Report/ARDetailReport.php b/app/Services/Report/ARDetailReport.php index c283a0a84ab0..3787acd07cd5 100644 --- a/app/Services/Report/ARDetailReport.php +++ b/app/Services/Report/ARDetailReport.php @@ -100,8 +100,6 @@ class ARDetailReport extends BaseExport $query = $this->filterByClients($query); - $this->csv->insertOne($this->buildHeader()); - $query->cursor() ->each(function ($invoice) { $this->csv->insertOne($this->buildRow($invoice)); diff --git a/app/Services/Report/ARSummaryReport.php b/app/Services/Report/ARSummaryReport.php index 58d509fbcf78..b561d0fbf538 100644 --- a/app/Services/Report/ARSummaryReport.php +++ b/app/Services/Report/ARSummaryReport.php @@ -122,7 +122,10 @@ class ARSummaryReport extends BaseExport ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->where('balance', '>', 0) ->where('is_deleted', 0) - ->where('due_date', '<', now()->startOfDay()) + ->where(function ($query){ + $query->where('due_date', '<', now()->startOfDay()) + ->orWhereNull('due_date'); + }) ->sum('balance'); return Number::formatMoney($amount, $this->client); diff --git a/app/Services/Scheduler/SchedulerService.php b/app/Services/Scheduler/SchedulerService.php index 1089b882d572..ddf327ab35a1 100644 --- a/app/Services/Scheduler/SchedulerService.php +++ b/app/Services/Scheduler/SchedulerService.php @@ -21,8 +21,6 @@ class SchedulerService use MakesHash; use MakesDates; - private string $method; - public function __construct(public Scheduler $scheduler) { } diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 032324a0a23e..6d433715c40a 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -56,7 +56,7 @@ class AccountTransformer extends EntityTransformer 'key' => (string) $account->key, 'default_url' => config('ninja.app_url'), 'plan' => $account->getPlan(), - 'plan_term' => (string) $account->plan_terms, + 'plan_term' => (string) $account->plan_term, 'plan_started' => (string) $account->plan_started, 'plan_paid' => (string) $account->plan_paid, 'plan_expires' => (string) $account->plan_expires, @@ -90,6 +90,8 @@ class AccountTransformer extends EntityTransformer 'set_react_as_default_ap' => (bool) $account->set_react_as_default_ap, 'trial_days_left' => Ninja::isHosted() ? (int) $account->getTrialDays() : 0, 'account_sms_verified' => (bool) $account->account_sms_verified, + 'has_iap_plan' => (bool)$account->inapp_transaction_id, + ]; } diff --git a/app/Transformers/SchedulerTransformer.php b/app/Transformers/SchedulerTransformer.php index ddef7bb15b51..cbc4af3d7e1e 100644 --- a/app/Transformers/SchedulerTransformer.php +++ b/app/Transformers/SchedulerTransformer.php @@ -27,7 +27,6 @@ class SchedulerTransformer extends EntityTransformer 'next_run' => $scheduler->next_run_client->format('Y-m-d'), 'template' => (string) $scheduler->template, 'is_paused' => (bool) $scheduler->is_paused, - 'is_deleted' => (bool) $scheduler->is_deleted, 'parameters'=> (array) $scheduler->parameters, 'is_deleted' => (bool) $scheduler->is_deleted, 'updated_at' => (int) $scheduler->updated_at, diff --git a/config/liap.php b/config/liap.php index cbc569b294b0..6a57acf72901 100644 --- a/config/liap.php +++ b/config/liap.php @@ -1,8 +1,17 @@ class_exists(\Modules\Admin\Listeners\Subscription\AppleAutoRenew::class) ? [\Modules\Admin\Listeners\Subscription\AppleAutoRenew::class] : [], SubscriptionRenewed::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleAutoRenew::class) ? [\Modules\Admin\Listeners\Subscription\GoogleAutoRenew::class] : [], - + DidChangeRenewalStatus::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleChangeRenewalStaus::class) ? [\Modules\Admin\Listeners\Subscription\GoogleChangeRenewalStaus::class] : [], + DidFailToRenew::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleFailedToRenew::class) ? [\Modules\Admin\Listeners\Subscription\GoogleFailedToRenew::class] : [], + Refund::class => class_exists(\Modules\Admin\Listeners\Subscription\AppleRefund::class) ? [\Modules\Admin\Listeners\Subscription\AppleRefund::class] : [], + SubscriptionRecovered::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRecovered::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRecovered::class] : [], + SubscriptionCanceled::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionCanceled::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionCanceled::class] : [], + SubscriptionPurchased::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPurchased::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPurchased::class] : [], + SubscriptionRestarted::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRestarted::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRestarted::class] : [], + SubscriptionPaused::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPaused::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPaused::class] : [], + SubscriptionRevoked::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRevoked::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRevoked::class] : [], + SubscriptionExpired::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionExpired::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionExpired::class] : [], ], /* diff --git a/config/ninja.php b/config/ninja.php index 420f57681116..c0a09c9c87d9 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.118', - 'app_tag' => '5.5.118', + 'app_version' => '5.5.119', + 'app_tag' => '5.5.119', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/lang/en/texts.php b/lang/en/texts.php index 009e9dee94e6..0d420fe76aea 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5087,6 +5087,13 @@ $LANG = array( 'light_dark_mode' => 'Light/Dark Mode', 'activities' => 'Activities', 'recent_transactions' => "Here are your company's most recent transactions:", + 'country_Palestine' => "Palestine", + 'country_Taiwan' => 'Taiwan', + 'duties' => 'Duties', + 'order_number' => 'Order Number', + 'order_id' => 'Order', + 'total_invoices_outstanding' => 'Total Invoices Outstanding', + 'recent_activity' => 'Recent Activity', ); diff --git a/routes/api.php b/routes/api.php index 0806d44c6ac5..1ce3e7136379 100644 --- a/routes/api.php +++ b/routes/api.php @@ -142,6 +142,9 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('charts/totals', [ChartController::class, 'totals'])->name('chart.totals'); Route::post('charts/chart_summary', [ChartController::class, 'chart_summary'])->name('chart.chart_summary'); + Route::post('charts/totals_v2', [ChartController::class, 'totalsV2'])->name('chart.totals_v2'); + Route::post('charts/chart_summary_v2', [ChartController::class, 'chart_summaryV2'])->name('chart.chart_summary_v2'); + Route::post('claim_license', [LicenseController::class, 'index'])->name('license.index'); Route::resource('clients', ClientController::class); // name = (clients. index / create / show / update / destroy / edit diff --git a/tests/Feature/TaskStatusApiTest.php b/tests/Feature/TaskStatusApiTest.php index 4ce96974699c..780ba4c12e0c 100644 --- a/tests/Feature/TaskStatusApiTest.php +++ b/tests/Feature/TaskStatusApiTest.php @@ -11,6 +11,7 @@ namespace Tests\Feature; +use App\Models\TaskStatus; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -28,6 +29,8 @@ class TaskStatusApiTest extends TestCase use DatabaseTransactions; use MockAccountData; + public $faker; + protected function setUp() :void { parent::setUp(); @@ -41,6 +44,38 @@ class TaskStatusApiTest extends TestCase Model::reguard(); } + public function testSorting() + { + TaskStatus::factory()->count(5)->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id + ]); + + + $t = TaskStatus::where('company_id', '=', $this->company->id)->orderBy('id', 'desc'); + + $this->assertEquals(10, $t->count()); + $task_status = $t->first(); + + $id = $task_status->id; + + nlog("setting {$id} to index 1"); + + $data = [ + 'status_order' => 1, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/task_statuses/'.$task_status->hashed_id, $data); + + $t = TaskStatus::where('company_id', '=', $this->company->id)->orderBy('status_order', 'asc')->first(); + + $this->assertEquals($id, $t->id); + + } + public function testTaskStatusGetFilter() { $response = $this->withHeaders([