v2 first commit

This commit is contained in:
denisdulici 2019-11-16 10:21:14 +03:00
parent 5b23e9c2c4
commit 6d50fa8442
3075 changed files with 3451681 additions and 65594 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at https://editorconfig.org
root = true
[*]
charset = utf-8
indent_size = 4
indent_style = space
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2

View File

@ -4,7 +4,6 @@ APP_LOCALE=en-GB
APP_INSTALLED=false
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_SCHEDULE_TIME="09:00"
DB_CONNECTION=mysql
@ -18,7 +17,8 @@ DB_PREFIX=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=database
QUEUE_CONNECTION=sync
LOG_CHANNEL=stack
MAIL_DRIVER=mail
MAIL_HOST=localhost
@ -28,3 +28,5 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_NAME=null
MAIL_FROM_ADDRESS=null
FIREWALL_ENABLED=false

View File

@ -4,7 +4,6 @@ APP_LOCALE=en-GB
APP_INSTALLED=false
APP_KEY=base64:xBC+BxlC7sXhYAtpTZv8TYAHqoPgsJaXL0S5Id6BbBc=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_SCHEDULE_TIME="09:00"
DB_CONNECTION=sqlite
@ -14,11 +13,14 @@ DB_PREFIX=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=database
QUEUE_CONNECTION=sync
LOG_CHANNEL=stack
MAIL_DRIVER=mail
MAIL_DRIVER=log
MAIL_HOST=localhost
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
FIREWALL_ENABLED=false

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
patreon: akaunting

4
.gitignore vendored
View File

@ -4,12 +4,14 @@
/storage/*.key
/vendor
/.idea
/.vscode
/.vagrant
Homestead.json
Homestead.yaml
npm-debug.log
.env
.env.example
robots.txt
_ide_helper.php
.phpstorm.meta.php
/storage/debugbar/*
/storage/debugbar/*

View File

@ -1,9 +1,6 @@
language: php
php:
- '5.6'
- '7.0'
- '7.1'
- '7.2'
- '7.3'

View File

@ -1,23 +0,0 @@
FROM php:apache
RUN apt-get update && apt-get install -y zip libzip-dev libpng-dev \
&& docker-php-ext-install pdo_mysql gd zip \
&& rm -rf /var/lib/apt/lists/*
# Composer installation.
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
RUN composer global require hirak/prestissimo --prefer-dist --no-progress --no-suggest --classmap-authoritative \
&& composer clear-cache
ENV PATH="${PATH}:/root/.composer/vendor/bin"
COPY . /var/www/html/
# Authorize these folders to be edited
RUN chmod -R 777 /var/www/html/storage
RUN chmod -R 777 /var/www/html/bootstrap/cache
# Allow rewrite
RUN a2enmod rewrite

View File

@ -1,8 +1,8 @@
# Akaunting™
![Latest Stable Version](https://img.shields.io/github/release/akaunting/akaunting.svg) ![Total Downloads](https://img.shields.io/github/downloads/akaunting/akaunting/total.svg) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/akaunting/localized.svg)](https://crowdin.com/project/akaunting) ![Build Status](https://travis-ci.com/akaunting/akaunting.svg) [![Backers on Open Collective](https://opencollective.com/akaunting/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/akaunting/sponsors/badge.svg)](#sponsors)
![Latest Stable Version](https://img.shields.io/github/release/akaunting/akaunting.svg) ![Total Downloads](https://img.shields.io/github/downloads/akaunting/akaunting/total.svg) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/akaunting/localized.svg)](https://crowdin.com/project/akaunting) ![Build Status](https://travis-ci.com/akaunting/akaunting.svg)
Akaunting is a free, open source and online accounting software designed for small businesses and freelancers. It is built with modern technologies such as Laravel, Bootstrap, jQuery, RESTful API etc. Thanks to its modular structure, Akaunting provides an awesome App Store for users and developers.
Akaunting is a free, open source and online accounting software designed for small businesses and freelancers. It is built with modern technologies such as Laravel, VueJS, Bootstrap 4, RESTful API etc. Thanks to its modular structure, Akaunting provides an awesome App Store for users and developers.
* [Home](https://akaunting.com) - The house of Akaunting
* [Blog](https://akaunting.com/blog) - Get the latest news
@ -12,51 +12,25 @@ Akaunting is a free, open source and online accounting software designed for sma
## Requirements
* PHP 5.6.4 or higher
* PHP 7.2 or higher
* Database (eg: MySQL, PostgreSQL, SQLite)
* Web Server (eg: Apache, Nginx, IIS)
* [Other libraries](https://akaunting.com/docs/requirements)
## Framework
Akaunting uses [Laravel](http://laravel.com), the best existing PHP framework, as the foundation framework and [Modules](https://nwidart.com/laravel-modules) package for Apps.
Akaunting uses [Laravel](http://laravel.com), the best existing PHP framework, as the foundation framework and [Module](https://github.com/akaunting/module) package for Apps.
## Installation
* Install [Composer](https://getcomposer.org/download)
* Download the [repository](https://github.com/akaunting/akaunting/archive/master.zip) and unzip into your server
* Open and point your command line to the directory you unzipped Akaunting
* Run the following command: `composer install`
* Finally, launch the [installer](https://akaunting.com/docs/installation)
## Docker
It is possible to containerise Akaunting using the [`docker-compose`](docker-compose.yml) file. Here are a few commands:
```
# Build the app
docker build -t akaunting .
# Run the app
docker-compose up -d
# Make sure you the dependencies are installed
docker-compose exec web composer install
# Stream logs
docker-compose logs -f web
# Access the container
docker-compose exec web /bin/sh
# Stop & Delete everything
docker-compose down -v
```
* Install [Composer](https://getcomposer.org/download)
* Download the [repository](https://github.com/akaunting/akaunting/archive/master.zip) and unzip into your server
* Open and point your command line to the directory you unzipped Akaunting
* Run the following command: `composer install`
* Finally, launch the [installer](https://akaunting.com/docs/installation)
## Contributing
Fork the repository, make the code changes then submit a pull request.
Please, be very clear on your commit messages and pull requests, empty pull request messages may be rejected without reason.
When contributing code to Akaunting, you must follow the PSR coding standards. The golden rule is: Imitate the existing Akaunting code.
@ -77,35 +51,19 @@ If you discover any security related issues, please email security@akaunting.com
## Credits
- [Denis Duliçi](https://github.com/denisdulici)
- [Cüneyt Şentürk](https://github.com/cuneytsenturk)
- [All Contributors](../../contributors)
* [Denis Duliçi](https://github.com/denisdulici)
* [Cüneyt Şentürk](https://github.com/cuneytsenturk)
* [All Contributors](../../contributors)
## Contributors
## Partners
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
[![Contributors](https://opencollective.com/akaunting/contributors.svg?width=890&button=false)](../../contributors)
Each of our partners can help you craft a beautiful, well-architected project. Feel free to get in [contact](https://akaunting.com/contact) with us to become a partner.
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/akaunting#backer)]
[![Backers](https://opencollective.com/akaunting/backers.svg?width=890)](https://opencollective.com/akaunting#backers)
* [Creative Tim](https://www.creative-tim.com) is our design partner since Akaunting 2.0 version. They create beautiful UI Kits, Templates, and Dashboards built on top of Bootstrap, Vue.js, React, Angular, Node.js, and Laravel.
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/akaunting#sponsor)]
[![Sponsor 0](https://opencollective.com/akaunting/sponsor/0/avatar.svg)](https://opencollective.com/akaunting/sponsor/0/website)
[![Sponsor 1](https://opencollective.com/akaunting/sponsor/1/avatar.svg)](https://opencollective.com/akaunting/sponsor/1/website)
[![Sponsor 2](https://opencollective.com/akaunting/sponsor/2/avatar.svg)](https://opencollective.com/akaunting/sponsor/2/website)
[![Sponsor 3](https://opencollective.com/akaunting/sponsor/3/avatar.svg)](https://opencollective.com/akaunting/sponsor/3/website)
[![Sponsor 4](https://opencollective.com/akaunting/sponsor/4/avatar.svg)](https://opencollective.com/akaunting/sponsor/4/website)
[![Sponsor 5](https://opencollective.com/akaunting/sponsor/5/avatar.svg)](https://opencollective.com/akaunting/sponsor/5/website)
[![Sponsor 6](https://opencollective.com/akaunting/sponsor/6/avatar.svg)](https://opencollective.com/akaunting/sponsor/6/website)
[![Sponsor 7](https://opencollective.com/akaunting/sponsor/7/avatar.svg)](https://opencollective.com/akaunting/sponsor/7/website)
[![Sponsor 8](https://opencollective.com/akaunting/sponsor/8/avatar.svg)](https://opencollective.com/akaunting/sponsor/8/website)
[![Sponsor 9](https://opencollective.com/akaunting/sponsor/9/avatar.svg)](https://opencollective.com/akaunting/sponsor/9/website)
Support Akaunting by becoming a sponsor on [Patreon](https://www.patreon.com/akaunting). Your logo will show up here with a link to your website.
## License

View File

@ -0,0 +1,162 @@
<?php
namespace App\Abstracts;
use Artisan;
use Illuminate\Database\Eloquent\Collection;
class BulkAction
{
public $model = false;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-common-items'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-common-items'
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-common-items'
]
];
/**
* Duplicate the specified resource.
*
* @param $request
*
* @return Response
*/
public function duplicate($request)
{
$selected = $request->get('selected', []);
$items = $this->model::find($selected);
foreach ($items as $item) {
$item->duplicate();
}
}
/**
* Enable the specified resource.
*
* @param $request
*
* @return Response
*/
public function enable($request)
{
$selected = $request->get('selected', []);
$items = $this->model::find($selected);
foreach ($items as $item) {
$item->enabled = 1;
$item->save();
}
}
/**
* Disable the specified resource.
*
* @param $request
*
* @return Response
*/
public function disable($request)
{
$selected = $request->get('selected', []);
$items = $this->model::find($selected);
foreach ($items as $item) {
$item->enabled = 0;
$item->save();
}
}
/**
* Remove the specified resource from storage.
*
* @param $request
*
* @return Response
*/
public function delete($request)
{
$this->destroy($request);
}
/**
* Remove the specified resource from storage.
*
* @param $request
*
* @return Response
*/
public function destroy($request)
{
$selected = $request->get('selected', []);
$items = $this->model::find($selected);
foreach ($items as $item) {
$item->delete();
}
Artisan::call('cache:clear');
}
public function countRelationships($model, $relationships)
{
$counter = [];
foreach ($relationships as $relationship => $text) {
if ($c = $model->$relationship()->count()) {
$counter[] = $c . ' ' . strtolower(trans_choice('general.' . $text, ($c > 1) ? 2 : 1));
}
}
return $counter;
}
/**
* Mass delete relationships with events being fired.
*
* @param $model
* @param $relationships
*
* @return void
*/
public function deleteRelationships($model, $relationships)
{
foreach ((array) $relationships as $relationship) {
if (empty($model->$relationship)) {
continue;
}
$items = $model->$relationship->all();
if ($items instanceof Collection) {
$items = $items->all();
}
foreach ((array) $items as $item) {
$item->delete();
}
}
}
}

View File

@ -1,12 +1,15 @@
<?php
namespace App\Http\Controllers;
namespace App\Abstracts\Http;
use Dingo\Api\Exception\ResourceException;
use Dingo\Api\Routing\Helpers;
use Illuminate\Http\Request;
class ApiController extends Controller
abstract class ApiController extends Controller
{
use Helpers;
/**
* Create the response for when a request fails validation.
*

View File

@ -0,0 +1,122 @@
<?php
namespace App\Abstracts\Http;
use App\Traits\Jobs;
use App\Traits\Relationships;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Routing\Route;
use Illuminate\Support\Str;
abstract class Controller extends BaseController
{
use AuthorizesRequests, Jobs, Relationships, ValidatesRequests;
/**
* Instantiate a new controller instance.
*/
public function __construct()
{
$this->setPermissions();
}
/**
* Assign permissions to methods.
*
* @return void
*/
protected function setPermissions()
{
// No need to check for permission in console
if (app()->runningInConsole()) {
return;
}
$route = app(Route::class);
// Get the controller array
$arr = array_reverse(explode('\\', explode('@', $route->getAction()['uses'])[0]));
$controller = '';
// Add folder
if (strtolower($arr[1]) != 'controllers') {
$controller .= Str::kebab($arr[1]) . '-';
}
// Add module
if (isset($arr[3]) && isset($arr[4]) && (strtolower($arr[4]) == 'modules')) {
$controller .= Str::kebab($arr[3]) . '-';
}
// Add file
$controller .= Str::kebab($arr[0]);
// Skip ACL
$skip = ['common-dashboard', 'portal-dashboard'];
if (in_array($controller, $skip)) {
return;
}
// Add CRUD permission check
$this->middleware('permission:create-' . $controller)->only(['create', 'store', 'duplicate', 'import']);
$this->middleware('permission:read-' . $controller)->only(['index', 'show', 'edit', 'export']);
$this->middleware('permission:update-' . $controller)->only(['update', 'enable', 'disable']);
$this->middleware('permission:delete-' . $controller)->only('destroy');
}
/**
* Generate a pagination collection.
*
* @param array|Collection $items
* @param int $perPage
* @param int $page
* @param array $options
*
* @return LengthAwarePaginator
*/
public function paginate($items, $perPage = 15, $page = null, $options = [])
{
$perPage = $perPage ?: request('limit', setting('default.list_limit', '25'));
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
}
/**
* Dispatch a job to its appropriate handler and return a response array for ajax calls.
*
* @param mixed $job
* @return mixed
*/
public function ajaxDispatch($job)
{
try {
$data = $this->dispatch($job);
$response = [
'success' => true,
'error' => false,
'data' => $data,
'message' => '',
];
} catch(\Exception $e) {
$response = [
'success' => false,
'error' => true,
'data' => null,
'message' => $e->getMessage(),
];
}
return $response;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Abstracts\Http;
use Illuminate\Foundation\Http\FormRequest as BaseFormRequest;
use Illuminate\Support\Arr;
abstract class FormRequest extends BaseFormRequest
{
/**
* Set the company id to the request.
*
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function getValidatorInstance()
{
// Get request data
$data = $this->all();
// Add active company id
$data['company_id'] = session('company_id');
// Reset the request data
$this->getInputSource()->replace($data);
return parent::getValidatorInstance();
}
/**
* Determine if the given offset exists.
*
* @param string $offset
* @return bool
*/
public function offsetExists($offset)
{
return Arr::has(
$this->route() ? $this->all() + $this->route()->parameters() : $this->all(),
$offset
);
}
}

View File

@ -0,0 +1,202 @@
<?php
namespace App\Abstracts\Http;
use App\Http\Requests\Portal\InvoicePayment as PaymentRequest;
use App\Models\Income\Invoice;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\URL;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
abstract class PaymentController extends BaseController
{
public $alias = '';
public $type = ''; // hosted, redirect
public $setting = [];
public $logger = null;
public $user = null;
public $module = null;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->setting = setting($this->alias);
$this->setting['code'] = $this->alias;
$this->setting['language'] = app()->getLocale();
$this->logger = $this->getLogger();
$this->user = user();
$this->module = module($this->alias);
return $next($request);
});
}
public function show(Invoice $invoice, PaymentRequest $request)
{
return $this->getInvoiceShow($invoice, 'show');
}
public function signed(Invoice $invoice, PaymentRequest $request)
{
return $this->getInvoiceShow($invoice, 'signed');
}
public function cancel(Invoice $invoice, $force_redirect = false)
{
$message = trans('messages.warning.payment_cancel', ['method' => setting($this->alias . '.name')]);
$this->logger->info($this->module->getName() . ':: Invoice: ' . $invoice->id . ' - Cancel Message: ' . $message);
flash($message)->warning();
$invoice_url = $this->getInvoiceUrl($invoice);
if ($force_redirect || ($this->type == 'redirect')) {
return redirect($invoice_url);
}
return response()->json([
'error' => $message,
'redirect' => $invoice_url,
'success' => false,
'data' => false,
]);
}
public function finish($invoice, $request, $force_redirect = false)
{
$this->dispatchPaidEvent($invoice, $request);
$this->forgetReference($invoice);
$message = trans('messages.success.added', ['type' => trans_choice('general.payments', 1)]);
$this->logger->info($this->module->getName() . ':: Invoice: ' . $invoice->id . ' - Success Message: ' . $message);
flash($message)->success();
$invoice_url = $this->getInvoiceUrl($invoice);
if ($force_redirect || ($this->type == 'redirect')) {
return redirect($invoice_url);
}
return response()->json([
'error' => $message,
'redirect' => $invoice_url,
'success' => true,
'data' => false,
]);
}
public function getInvoiceShow(Invoice $invoice, $view = 'show')
{
$this->setContactFirstLastName($invoice);
$confirm_url = $this->getConfirmUrl($invoice);
$html = view('partials.portal.payment_method.' . $this->type . '.' . $view, [
'setting' => $this->setting,
'invoice' => $invoice,
'confirm_url' => $confirm_url,
])->render();
return response()->json([
'code' => $this->setting['code'],
'name' => $this->setting['name'],
'description' => trans($this->alias . '::general.description'),
'redirect' => false,
'html' => $html,
]);
}
public function getInvoiceUrl($invoice)
{
return $this->user
? route('portal.invoices.show', $invoice->id)
: URL::signedRoute('signed.invoices.show', [$invoice->id, 'company_id' => $invoice->company_id]);
}
public function getConfirmUrl($invoice)
{
return $this->getModuleUrl($invoice, 'confirm');
}
public function getReturnUrl($invoice)
{
return $this->getModuleUrl($invoice, 'return');
}
public function getCancelUrl($invoice)
{
return $this->getModuleUrl($invoice, 'cancel');
}
public function getNotifyUrl($invoice)
{
return route('portal.invoices.' . $this->alias . '.notify', $invoice->id);
}
public function getModuleUrl($invoice, $suffix)
{
return $this->user
? route('portal.invoices.' . $this->alias . '.' . $suffix, $invoice->id)
: URL::signedRoute('signed.invoices.' . $this->alias . '.' . $suffix, [$invoice->id, 'company_id' => $invoice->company_id]);
}
public function getLogger()
{
$log = new Logger($this->alias);
$log->pushHandler(new StreamHandler(storage_path('logs/' . $this->alias . '.log')), Logger::INFO);
return $log;
}
public function dispatchPaidEvent($invoice, $request)
{
$request['company_id'] = $invoice->company_id;
$request['amount'] = $invoice->amount;
$request['payment_method'] = $this->alias;
$request['reference'] = $this->getReference($invoice);
event(new \App\Events\Income\PaymentReceived($invoice, $request));
}
public function setReference($invoice, $reference)
{
session([
$this->alias . '_' . $invoice->id . '_reference' => $reference
]);
}
public function getReference($invoice)
{
return session($this->alias . '_' . $invoice->id . '_reference');
}
public function forgetReference($invoice)
{
session()->forget($this->alias . '_' . $invoice->id . '_reference');
}
public function setContactFirstLastName(&$invoice)
{
$contact = explode(" ", $invoice->contact_name);
$last_name = array_pop($contact);
$first_name = implode(" ", $contact);
$invoice->first_name = $first_name;
$invoice->last_name = $last_name;
}
}

28
app/Abstracts/Job.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace App\Abstracts;
use App\Abstracts\Http\FormRequest;
use App\Traits\Jobs;
use App\Traits\Relationships;
use App\Traits\Uploads;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
abstract class Job implements ShouldQueue
{
use InteractsWithQueue, Jobs, Queueable, Relationships, SerializesModels, Uploads;
public function getRequestInstance($request)
{
if (!is_array($request)) {
return $request;
}
$class = new class() extends FormRequest {};
return $class->merge($request);
}
}

View File

@ -1,18 +1,16 @@
<?php
namespace App\Models;
namespace App\Abstracts;
use App\Scopes\Company;
use EloquentFilter\Filterable;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Eloquent\SoftDeletes;
use Kyslik\ColumnSortable\Sortable;
use Request;
use Route;
use Lorisleiva\LaravelSearchString\Concerns\SearchString;
class Model extends Eloquent
abstract class Model extends Eloquent
{
use Filterable, SoftDeletes, Sortable;
use SearchString, SoftDeletes, Sortable;
protected $dates = ['deleted_at'];
@ -38,31 +36,6 @@ class Model extends Eloquent
return $this->belongsTo('App\Models\Common\Company');
}
/**
* Define the filter provider globally.
*
* @return ModelFilter
*/
public function modelFilter()
{
// Check if is api or web
if (Request::is('api/*')) {
$arr = array_reverse(explode('\\', explode('@', app()['api.router']->currentRouteAction())[0]));
$folder = $arr[1];
$file = $arr[0];
} else {
list($folder, $file) = explode('/', Route::current()->uri());
}
if (empty($folder) || empty($file)) {
return $this->provideFilter();
}
$class = '\App\Filters\\' . ucfirst($folder) . '\\' . ucfirst($file);
return $this->provideFilter($class);
}
/**
* Scope to only include company data.
*
@ -88,10 +61,10 @@ class Model extends Eloquent
{
$request = request();
$input = $request->input();
$limit = $request->get('limit', setting('general.list_limit', '25'));
$search = $request->get('search');
$limit = $request->get('limit', setting('default.list_limit', '25'));
return $query->filter($input)->sortable($sort)->paginate($limit);
return $query->usingSearchString($search)->sortable($sort)->paginate($limit);
}
/**
@ -131,27 +104,18 @@ class Model extends Eloquent
public function scopeAccount($query, $accounts)
{
if (empty($accounts)) {
return;
return $query;
}
return $query->whereIn('account_id', (array) $accounts);
}
public function scopeCustomer($query, $customers)
public function scopeContact($query, $contacts)
{
if (empty($customers)) {
return;
if (empty($contacts)) {
return $query;
}
return $query->whereIn('customer_id', (array) $customers);
}
public function scopeVendor($query, $vendors)
{
if (empty($vendors)) {
return;
}
return $query->whereIn('vendor_id', (array) $vendors);
return $query->whereIn('contact_id', (array) $contacts);
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Abstracts;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification as BaseNotification;
abstract class Notification extends BaseNotification
{
/**
* Create a notification instance.
*/
public function __construct()
{
$this->queue = 'high';
$this->delay = config('queue.connections.database.delay');
}
/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return ['mail', 'database'];
}
/**
* Initialise the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function initMessage()
{
$message = (new MailMessage)
->from(config('mail.from.address'), config('mail.from.name'))
->subject($this->getSubject())
->view('partials.email.body', ['body' => $this->getBody()]);
return $message;
}
public function getSubject()
{
$content = setting('email.' . $this->template . '_subject');
return $this->replaceTags($content);
}
public function getBody()
{
$content = setting('email.' . $this->template . '_body');
return $this->replaceTags($content);
}
public function replaceTags($content)
{
return preg_replace($this->getTagsPattern(), $this->getTagsReplacement(), $content);
}
public function getTagsPattern()
{
$pattern = [];
foreach($this->getTags() as $tag) {
$pattern[] = "/" . $tag . "/";
}
return $pattern;
}
}

View File

@ -0,0 +1,226 @@
<?php
namespace App\Abstracts\Reports;
use App\Events\Common\ReportFilterApplying;
use App\Events\Common\ReportFilterShowing;
use App\Events\Common\ReportGroupApplying;
use App\Events\Common\ReportGroupShowing;
use App\Models\Banking\Account;
use App\Models\Common\Contact;
use App\Models\Setting\Category;
use App\Traits\Contacts;
use Date;
abstract class Listener
{
use Contacts;
protected $class = '';
protected $events = [
'App\Events\Common\ReportFilterShowing',
'App\Events\Common\ReportFilterApplying',
'App\Events\Common\ReportGroupShowing',
'App\Events\Common\ReportGroupApplying',
];
public function checkClass($event)
{
return (get_class($event->class) == $this->class);
}
public function getYears()
{
$now = Date::now();
$years = [];
$y = $now->addYears(2);
for ($i = 0; $i < 10; $i++) {
$years[$y->year] = $y->year;
$y->subYear();
}
return $years;
}
public function getAccounts()
{
return Account::enabled()->orderBy('name')->pluck('name', 'id')->toArray();
}
public function getItemCategories()
{
return $this->getCategories('item');
}
public function getIncomeCategories()
{
return $this->getCategories('income');
}
public function getExpenseCategories()
{
return $this->getCategories('expense');
}
public function getIncomeExpenseCategories()
{
return $this->getCategories(['income', 'expense']);
}
public function getCategories($types)
{
return Category::type($types)->enabled()->orderBy('name')->pluck('name', 'id')->toArray();
}
public function getCustomers()
{
return $this->getContacts($this->getCustomerTypes());
}
public function getVendors()
{
return $this->getContacts($this->getVendorTypes());
}
public function getContacts($types)
{
return Contact::type($types)->enabled()->orderBy('name')->pluck('name', 'id')->toArray();
}
public function applyDateFilter($event)
{
$event->model->monthsOfYear($event->args['date_field']);
}
public function applySearchStringFilter($event)
{
$event->model->usingSearchString(request('search'));
}
public function applyAccountGroup($event)
{
if (($event->model->getTable() != 'invoices') && ($event->model->getTable() != 'bills')) {
return;
}
$filter = request('accounts', []);
$event->model->account_id = 0;
foreach ($event->model->transactions as $transaction) {
if (!empty($filter) && !in_array($transaction->account_id, $filter)) {
continue;
}
$event->model->account_id = $transaction->account_id;
}
}
public function applyCustomerGroup($event)
{
foreach ($this->getCustomerTypes() as $type) {
$id_field = $type . '_id';
$event->model->$id_field = $event->model->contact_id;
}
}
public function applyVendorGroup($event)
{
foreach ($this->getVendorTypes() as $type) {
$id_field = $type . '_id';
$event->model->$id_field = $event->model->contact_id;
}
}
/**
* Handle filter showing event.
*
* @param $event
* @return void
*/
public function handleReportFilterShowing(ReportFilterShowing $event)
{
if (!$this->checkClass($event)) {
return;
}
$event->class->filters['years'] = $this->getYears();
}
/**
* Handle filter applying event.
*
* @param $event
* @return void
*/
public function handleReportFilterApplying(ReportFilterApplying $event)
{
if (!$this->checkClass($event)) {
return;
}
// Apply date
$this->applyDateFilter($event);
// Apply search
$this->applySearchStringFilter($event);
}
/**
* Handle group showing event.
*
* @param $event
* @return void
*/
public function handleReportGroupShowing(ReportGroupShowing $event)
{
if (!$this->checkClass($event)) {
return;
}
$event->class->groups['category'] = trans_choice('general.categories', 1);
}
/**
* Handle group applying event.
*
* @param $event
* @return void
*/
public function handleReportGroupApplying(ReportGroupApplying $event)
{
if (!$this->checkClass($event)) {
return;
}
$this->applyAccountGroup($event);
}
/**
* Register the listeners for the subscriber.
*
* @param \Illuminate\Events\Dispatcher $events
*/
public function subscribe($events)
{
$class = get_class($this);
foreach ($this->events as $event) {
$method = 'handle' . (new \ReflectionClass($event))->getShortName();
if (!method_exists($class, $method)) {
continue;
}
$events->listen(
$event,
$class . '@' . $method
);
}
}
}

View File

@ -0,0 +1,434 @@
<?php
namespace App\Abstracts\Reports;
use App\Exports\Common\Reports as Export;
use App\Models\Common\Report as Model;
use App\Utilities\Chartjs;
use App\Traits\DateTime;
use Date;
use Illuminate\Support\Str;
abstract class Report
{
use DateTime;
public $report;
public $year;
public $views = [];
public $tables = [];
public $dates = [];
public $rows = [];
public $totals = [];
public $groups = [];
public $filters = [];
public $category = 'income-expense';
public $icon = 'fa fa-chart-pie';
public $indents = [
'table_header' => '0px',
'table_rows' => '0px',
];
public $chart = [
'line' => [
'width' => '0',
'height' => '300',
'options' => [
'color' => '#6da252',
],
],
'dates' => [],
'datasets' => [],
];
public function __construct(Model $report = null, $get_totals = true)
{
$this->setGroups();
if (!$report) {
return;
}
$this->report = $report;
$this->setYear();
$this->setViews();
$this->setTables();
$this->setDates();
$this->setFilters();
$this->setRows();
if ($get_totals) {
$this->getTotals();
}
}
abstract public function getTotals();
public function getName()
{
return Str::title(str_replace('_', ' ', Str::snake((new \ReflectionClass($this))->getShortName())));
}
public function getCategory()
{
return $this->category;
}
public function getIcon()
{
return $this->icon;
}
public function getTotal()
{
$sum = 0;
foreach ($this->totals as $total) {
$sum += is_array($total) ? array_sum($total) : $total;
}
$total = money($sum, setting('default.currency'), true);
return $total;
}
public function getTableRowList()
{
$group_prl = Str::plural($this->report->group);
if ($group_filter = request($group_prl)) {
$rows = collect($this->filters[$group_prl])->filter(function ($value, $key) use ($group_filter) {
return in_array($key, $group_filter);
});
} else {
$rows = $this->filters[$group_prl];
}
return $rows;
}
public function getChart()
{
$chart = new Chartjs();
$config = $this->chart[$this->report->chart];
$default_options = [
'tooltips' => [
'backgroundColor' => '#f5f5f5',
'titleFontColor' => '#333',
'bodyFontColor' => '#666',
'bodySpacing' => 4,
'YrPadding' => 12,
'mode' => 'nearest',
'intersect' => 0,
'position' => 'nearest'
],
'responsive' => true,
'scales' => [
'yAxes' => [
[
'barPercentage' => '1.6',
'gridLines' => [
'drawBorder' => false,
'color' => 'rgba(29,140,248,0.1)',
'zeroLineColor' => 'transparent',
'borderDash' => [2],
'borderDashOffset' => [2],
],
'ticks' => [
'padding' => 10,
'fontColor' => '#9e9e9e'
]
]
],
'xAxes' => [
[
'barPercentage' => '1.6',
'gridLines' => [
'drawBorder' => false,
'color' => 'rgba(29,140,248,0.0)',
'zeroLineColor' => 'transparent'
],
'ticks' => [
'suggestedMin' => 60,
'suggestedMax' => 125,
'padding' => 20,
'fontColor' => '#9e9e9e'
]
]
]
]
];
$options = array_merge($default_options, (array) $config['options']);
$chart->type($this->report->chart)
->width((int) $config['width'])
->height((int) $config['height'])
->options($options)
->labels(!empty($config['dates']) ? array_values($config['dates']) : array_values($this->dates));
if (!empty($config['datasets'])) {
foreach ($config['datasets'] as $dataset) {
$chart->dataset($dataset['name'], 'line', array_values($dataset['totals']))
->backgroundColor(isset($dataset['backgroundColor']) ? $dataset['backgroundColor'] : '#6da252')
->color(isset($dataset['color']) ? $dataset['color'] : '#6da252')
->options((array) $dataset['options'])
->fill(false);
}
} else {
foreach ($this->totals as $total) {
$chart->dataset($this->report->name, 'line', array_values($total))
->backgroundColor(isset($config['backgroundColor']) ? $config['backgroundColor'] : '#6da252')
->color(isset($config['color']) ? $config['color'] : '#6da252')
->options([
'borderWidth' => 4,
'pointStyle' => 'line',
])
->fill(false);
}
}
return $chart;
}
public function show()
{
return view($this->views['show'])->with('class', $this);
}
public function print()
{
return view($this->views['print'])->with('class', $this);
}
public function export()
{
return \Excel::download(new Export($this->views['content'], $this), $this->report->name . '.xlsx');
}
public function setYear()
{
$this->year = request('year', Date::now()->year);
}
public function setViews()
{
$this->views = [
'chart' => 'partials.reports.chart',
'content' => 'partials.reports.content',
'content.header' => 'partials.reports.content.header',
'content.footer' => 'partials.reports.content.footer',
'show' => 'partials.reports.show',
'header' => 'partials.reports.header',
'filter' => 'partials.reports.filter',
'print' => 'partials.reports.print',
'table' => 'partials.reports.table',
'table.footer' => 'partials.reports.table.footer',
'table.header' => 'partials.reports.table.header',
'table.rows' => 'partials.reports.table.rows',
];
}
public function setTables()
{
$this->tables = [
'default' => 'default'
];
}
public function setDates()
{
$function = 'sub' . ucfirst(str_replace('ly', '', $this->report->period));
$start = $this->getFinancialStart()->copy()->$function();
for ($j = 1; $j <= 12; $j++) {
switch ($this->report->period) {
case 'yearly':
$start->addYear();
$date = $this->getFormattedDate($start);
$this->dates[$j] = $date;
foreach ($this->tables as $table) {
$this->totals[$table][$date] = 0;
}
$j += 11;
break;
case 'quarterly':
$start->addQuarter();
$date = $this->getFormattedDate($start);
$this->dates[$j] = $date;
foreach ($this->tables as $table) {
$this->totals[$table][$date] = 0;
}
$j += 2;
break;
default:
$start->addMonth();
$date = $this->getFormattedDate($start);
$this->dates[$j] = $date;
foreach ($this->tables as $table) {
$this->totals[$table][$date] = 0;
}
break;
}
}
}
public function setFilters()
{
event(new \App\Events\Common\ReportFilterShowing($this));
}
public function setGroups()
{
event(new \App\Events\Common\ReportGroupShowing($this));
}
public function setRows()
{
$list = $this->getTableRowList();
foreach ($this->dates as $date) {
foreach ($this->tables as $table) {
foreach ($list as $id => $name) {
$this->rows[$table][$id][$date] = 0;
}
}
}
}
public function setTotals($items, $date_field, $check_type = false, $table = 'default')
{
foreach ($items as $item) {
// Make groups extensible
$item = $this->applyGroups($item);
$date = $this->getFormattedDate(Date::parse($item->$date_field));
$id_field = $this->report->group . '_id';
if (!isset($this->rows[$table][$item->$id_field]) ||
!isset($this->rows[$table][$item->$id_field][$date]) ||
!isset($this->totals[$table][$date]))
{
continue;
}
$amount = $item->getAmountConvertedToDefault();
if (!$check_type) {
$this->rows[$table][$item->$id_field][$date] += $amount;
$this->totals[$table][$date] += $amount;
} else {
$type = (($item->getTable() == 'invoices') || (($item->getTable() == 'transactions') && ($item->type == 'income'))) ? 'income' : 'expense';
if ($type == 'income') {
$this->rows[$table][$item->$id_field][$date] += $amount;
$this->totals[$table][$date] += $amount;
} else {
$this->rows[$table][$item->$id_field][$date] -= $amount;
$this->totals[$table][$date] -= $amount;
}
}
}
}
public function applyFilters($model, $args = [])
{
event(new \App\Events\Common\ReportFilterApplying($this, $model, $args));
return $model;
}
public function applyGroups($model, $args = [])
{
event(new \App\Events\Common\ReportGroupApplying($this, $model, $args));
return $model;
}
public function getFormattedDate($date)
{
switch ($this->report->period) {
case 'yearly':
$i = $date->copy()->format($this->getYearlyDateFormat());
break;
case 'quarterly':
$start = $date->copy()->startOfQuarter()->format($this->getQuarterlyDateFormat());
$end = $date->copy()->endOfQuarter()->format($this->getQuarterlyDateFormat());
$i = $start . '-' . $end;
break;
default:
$i = $date->copy()->format($this->getMonthlyDateFormat());
break;
}
return $i;
}
public function getUrl($action = 'print')
{
$print_url = 'common/reports/' . $this->report->id . '/' . $action . '?year='. $this->year;
collect(request('accounts'))->each(function($item) use(&$print_url) {
$print_url .= '&accounts[]=' . $item;
});
collect(request('customers'))->each(function($item) use(&$print_url) {
$print_url .= '&customers[]=' . $item;
});
collect(request('categories'))->each(function($item) use(&$print_url) {
$print_url .= '&categories[]=' . $item;
});
return $print_url;
}
public function getPermission()
{
$permission = 'read-reports-' . Str::kebab((new \ReflectionClass($this))->getShortName());
return $permission;
}
public function canRead()
{
return user()->can($this->getPermission());
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\BulkActions\Auth;
use App\Abstracts\BulkAction;
use App\Models\Auth\Permission;
class Permissions extends BulkAction
{
public $model = Permission::class;
public $actions = [
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-auth-permissions'
]
];
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\BulkActions\Auth;
use App\Abstracts\BulkAction;
use App\Models\Auth\Role;
class Roles extends BulkAction
{
public $model = Role::class;
public $actions = [
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-auth-roles'
]
];
}

View File

@ -0,0 +1,78 @@
<?php
namespace App\BulkActions\Auth;
use App\Abstracts\BulkAction;
use App\Models\Auth\User;
use Artisan;
class Users extends BulkAction
{
public $model = User::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-auth-users'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-auth-users'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-auth-users'
]
];
public function disable($request)
{
$selected = $request->get('selected', []);
$users = $this->model::find($selected);
foreach ($users as $user) {
// Can't disable yourself
if ($user->id == user()->id) {
continue;
//$this->response->errorMethodNotAllowed(trans('auth.error.self_delete'));
}
$user->enabled = 0;
$user->save();
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$users = $this->model::find($selected);
foreach ($users as $user) {
// Can't delete yourself
if ($user->id == user()->id) {
continue;
//$this->response->errorMethodNotAllowed(trans('auth.error.self_delete'));
}
$user->delete();
}
// Can't delete yourself
if ($user->id == app(Auth::class)->user()->id) {
$this->response->errorMethodNotAllowed(trans('auth.error.self_delete'));
}
Artisan::call('cache:clear');
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace App\BulkActions\Banking;
use App\Abstracts\BulkAction;
use App\Models\Banking\Account;
class Accounts extends BulkAction
{
public $model = Account::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-banking-accounts'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-banking-accounts'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-banking-accounts'
]
];
public function disable($request)
{
$selected = $request->get('selected', []);
$accounts = $this->model::find($selected);
foreach ($accounts as $account) {
if ($account->id == setting('default.account')) {
$relationships[] = strtolower(trans_choice('general.companies', 1));
}
if (empty($relationships)) {
$account->enabled = 0;
$account->save();
$message = trans('messages.success.disabled', ['type' => $account->name]);
return $this->itemResponse($account->fresh(), new Transformer(), $message);
} else {
$message = trans('messages.warning.disabled', ['name' => $account->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$accounts = $this->model::find($selected);
foreach ($accounts as $account) {
if ($relationships = $this->getRelationships($account)) {
if ($account->id == setting('default.account')) {
$relationships[] = strtolower(trans_choice('general.companies', 1));
}
}
if (empty($relationships)) {
$account->delete();
$message = trans('messages.success.deleted', ['type' => $account->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $account->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
protected function getRelationships($account)
{
$relationships = $this->countRelationships($account, [
'payments' => 'payments',
'revenues' => 'revenues',
]);
return $relationships;
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace App\BulkActions\Banking;
use App\Abstracts\BulkAction;
use App\Models\Banking\Reconciliation;
use App\Models\Banking\Transaction;
class Reconciliations extends BulkAction
{
public $model = Reconciliation::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-banking-reconciliations'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-banking-reconciliations'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-banking-reconciliations'
]
];
public function enable($request)
{
$selected = $request->get('selected', []);
$reconciliations = $this->model::find($selected);
foreach ($reconciliations as $reconciliation) {
$reconciliation->enabled = 1;
$reconciliation->save();
Transaction::where('account_id', $reconciliation->account_id)
->reconciled()
->whereBetween('paid_at', [$reconciliation->started_at, $reconciliation->ended_at])->each(function ($item) {
$item->reconciled = 1;
$item->save();
});
}
}
public function disable($request)
{
$selected = $request->get('selected', []);
$reconciliations = $this->model::find($selected);
foreach ($reconciliations as $reconciliation) {
$reconciliation->enabled = 0;
$reconciliation->save();
Transaction::where('account_id', $reconciliation->account_id)
->reconciled()
->whereBetween('paid_at', [$reconciliation->started_at, $reconciliation->ended_at])->each(function ($item) {
$item->reconciled = 0;
$item->save();
});
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$reconciliations = $this->model::find($selected);
foreach ($reconciliations as $reconciliation) {
$reconciliation->delete();
Transaction::where('account_id', $reconciliation->account_id)
->reconciled()
->whereBetween('paid_at', [$reconciliation->started_at, $reconciliation->ended_at])->each(function ($item) {
$item->reconciled = 0;
$item->save();
});
}
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\BulkActions\Banking;
use App\Abstracts\BulkAction;
use App\Models\Banking\Transfer;
class Transfers extends BulkAction
{
public $model = Transfer::class;
public $actions = [
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_action.message.deletes',
'permission' => 'delete-banking-transfers'
]
];
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$transfers = $this->model::find($selected);
foreach ($transfers as $transfer) {
$this->deleteRelationships($transfer, ['expense_transaction', 'income_transaction']);
$transfer->delete();
}
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace App\BulkActions\Common;
use App\Abstracts\BulkAction;
use App\Models\Common\Company;
class Companies extends BulkAction
{
public $model = Company::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-common-companies'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-common-companies'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-common-companies'
]
];
public function enable($request)
{
try {
$selected = $request->get('selected', []);
$companies = $this->model::find($selected);
foreach ($companies as $company) {
// Check if user can access company
$this->owner($company);
$company->enabled = 1;
$company->save();
}
$message = trans('messages.success.enabled', ['type' => $company->name]);
return $this->itemResponse($company->fresh(), new Transformer(), $message);
} catch (\HttpException $e) {
$this->response->errorUnauthorized(trans('companies.error.not_user_company'));
}
}
public function disable($request)
{
try {
$selected = $request->get('selected', []);
$companies = $this->model::find($selected);
foreach ($companies as $company) {
// Check if user can access company
$this->owner($company);
$company->enabled = 0;
$company->save();
}
} catch (\HttpException $e) {
$this->response->errorUnauthorized(trans('companies.error.not_user_company'));
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$companies = $this->model::find($selected);
foreach ($companies as $company) {
// Can't delete active company
if ($company->id == session('company_id')) {
$content = trans('companies.error.delete_active');
return new Response($content, 422);
}
try {
// Check if user can access company
$this->owner($company);
$company->delete();
$message = trans('messages.success.deleted', ['type' => $company->name]);
return new Response($message);
} catch (\HttpException $e) {
$this->response->errorUnauthorized(trans('companies.error.not_user_company'));
}
}
}
public function owner(Company $company)
{
$companies = user()->companies()->pluck('id')->toArray();
if (in_array($company->id, $companies)) {
return new Response('');
}
$this->response->errorUnauthorized(trans('companies.error.not_user_company'));
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace App\BulkActions\Common;
use App\Abstracts\BulkAction;
use App\Exports\Common\Items as Export;
use App\Models\Common\Item;
class Items extends BulkAction
{
public $model = Item::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-common-items'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-common-items'
],
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-common-items',
'multiple' => true
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-common-items'
]
];
/**
* Remove the specified resource from storage.
*
* @param $id
*
* @return Response
*/
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$items = $this->model::find($selected);
foreach ($items as $item) {
$relationships = $this->countRelationships($item, [
'invoice_items' => 'invoices',
'bill_items' => 'bills',
]);
if (empty($relationships)) {
$item->delete();
$message = trans('messages.success.deleted', ['type' => $item->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $item->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
/**
* Export the specified resource.
*
* @return Response
*/
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.items', 2) . '.xlsx');
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace App\BulkActions\Expenses;
use App\Abstracts\BulkAction;
use App\Exports\Expenses\Bills as Export;
use App\Models\Expense\Bill;
use App\Models\Expense\BillHistory;
class Bills extends BulkAction
{
public $model = Bill::class;
public $actions = [
'received' => [
'name' => 'general.received',
'message' => '',
'permission' => 'update-expenses-bills'
],
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-expenses-bills',
'multiple' => true
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-expenses-bills'
]
];
public function duplicate($request)
{
$selected = $request->get('selected', []);
$bills = $this->model::find($selected);
foreach ($bills as $bill) {
$clone = $bill->duplicate();
// Add bill history
BillHistory::create([
'company_id' => session('company_id'),
'bill_id' => $clone->id,
'status_code' => 'draft',
'notify' => 0,
'description' => trans('messages.success.added', ['type' => $clone->bill_number]),
]);
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$bills = $this->model::find($selected);
foreach ($bills as $bill) {
$this->deleteRelationships($bill, ['items', 'item_taxes', 'histories', 'payments', 'recurring', 'totals']);
$bill->delete();
}
}
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.bills', 2) . '.xlsx');
}
public function received($request)
{
$selected = $request->get('selected', []);
$bills = $this->model::find($selected);
foreach ($bills as $bill) {
$bill->bill_status_code = 'received';
$bill->save();
// Add bill history
BillHistory::create([
'company_id' => $bill->company_id,
'bill_id' => $bill->id,
'status_code' => 'received',
'notify' => 0,
'description' => trans('bills.mark_recevied'),
]);
}
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace App\BulkActions\Expenses;
use App\Abstracts\BulkAction;
use App\Exports\Expenses\Payments as Export;
use App\Models\Banking\Transaction;
use App\Models\Setting\Category;
class Payments extends BulkAction
{
public $model = Transaction::class;
public $actions = [
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-expenses-payments',
'multiple' => true
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-expenses-payments'
]
];
public function duplicate($request)
{
$selected = $request->get('selected', []);
$transactions = $this->model::find($selected);
foreach ($transactions as $transaction) {
$clone = $transaction->duplicate();
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$transactions = $this->model::find($selected);
foreach ($transactions as $transaction) {
if ($transaction->category->id != Category::transfer()) {
$type = $transaction->type;
$transaction->recurring()->delete();
$transaction->delete();
$message = trans('messages.success.deleted', ['type' => trans_choice('general.' . \Str::plural($type), 1)]);
return new Response($message);
} else {
$this->response->errorUnauthorized();
}
}
}
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.payments', 2) . '.xlsx');
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace App\BulkActions\Expenses;
use App\Abstracts\BulkAction;
use App\Exports\Expenses\Vendors as Export;
use App\Models\Common\Contact;
class Vendors extends BulkAction
{
public $model = Contact::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-expenses-vendors'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-expenses-vendors'
],
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-expenses-vendors',
'multiple' => true
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-expenses-vendors'
]
];
public function duplicate($request)
{
$selected = $request->get('selected', []);
$contacts = $this->model::find($selected);
foreach ($contacts as $contact) {
$clone = $contact->duplicate();
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$contacts = $this->model::find($selected);
foreach ($contacts as $contact) {
if (!$relationships = $this->getRelationships($contact)) {
$contact->delete();
$message = trans('messages.success.deleted', ['type' => $contact->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $contact->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.vendors', 2) . '.xlsx');
}
protected function getRelationships($contact)
{
$rels = [
'bills' => 'bills',
'payments' => 'payments',
];
$relationships = $this->countRelationships($contact, $rels);
return $relationships;
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace App\BulkActions\Incomes;
use App\Abstracts\BulkAction;
use App\Exports\Incomes\Customers as Export;
use App\Models\Common\Contact;
class Customers extends BulkAction
{
public $model = Contact::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-incomes-customers'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-incomes-customers'
],
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-incomes-customers',
'multiple' => true
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.export',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-incomes-customers'
]
];
public function duplicate($request)
{
$selected = $request->get('selected', []);
$contacts = $this->model::find($selected);
foreach ($contacts as $contact) {
$clone = $contact->duplicate();
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$contacts = $this->model::find($selected);
foreach ($contacts as $contact) {
if (!$relationships = $this->getRelationships($contact)) {
$contact->delete();
$message = trans('messages.success.deleted', ['type' => $contact->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $contact->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.customers', 2) . '.xlsx');
}
protected function getRelationships($contact)
{
$rels = [
'invoices' => 'invoices',
'revenues' => 'revenues',
];
$relationships = $this->countRelationships($contact, $rels);
return $relationships;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace App\BulkActions\Incomes;
use App\Abstracts\BulkAction;
use App\Events\Income\InvoiceCreated;
use App\Events\Income\InvoiceSent;
use App\Events\Income\PaymentReceived;
use App\Exports\Incomes\Invoices as Export;
use App\Models\Income\Invoice;
use Date;
class Invoices extends BulkAction
{
public $model = Invoice::class;
public $actions = [
'paid' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-incomes-invoices'
],
'sent' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-incomes-invoices'
],
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-incomes-invoices',
'multiple' => true
],
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-incomes-invoices'
]
];
public function duplicate($request)
{
$selected = $request->get('selected', []);
$invoices = $this->model::find($selected);
foreach ($invoices as $invoice) {
$clone = $invoice->duplicate();
event(new InvoiceCreated($clone));
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$invoices = $this->model::find($selected);
foreach ($invoices as $invoice) {
$this->deleteRelationships($invoice, ['items', 'item_taxes', 'histories', 'transactions', 'recurring', 'totals']);
$invoice->delete();
}
}
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.invoices', 2) . '.xlsx');
}
public function sent($request)
{
$selected = $request->get('selected', []);
$invoices = $this->model::find($selected);
foreach ($invoices as $invoice) {
event(new InvoiceSent($invoice));
$message = trans('invoices.messages.marked_sent');
}
}
public function paid($request)
{
$selected = $request->get('selected', []);
$invoices = $this->model::find($selected);
foreach ($invoices as $invoice) {
event(new PaymentReceived($invoice, []));
}
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace App\BulkActions\Incomes;
use App\Abstracts\BulkAction;
use App\Exports\Incomes\Revenues as Export;
use App\Models\Banking\Transaction;
use App\Models\Setting\Category;
class Revenues extends BulkAction
{
public $model = Transaction::class;
public $actions = [
'export' => [
'name' => 'general.export',
'message' => 'bulk_actions.message.exports',
],
'duplicate' => [
'name' => 'general.duplicate',
'message' => 'bulk_actions.message.duplicate',
'permission' => 'create-incomes-revenues',
'multiple' => true
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-incomes-revenues'
]
];
public function duplicate($request)
{
$selected = $request->get('selected', []);
$transactions = $this->model::find($selected);
foreach ($transactions as $transaction) {
$clone = $transaction->duplicate();
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$transactions = $this->model::find($selected);
foreach ($transactions as $transaction) {
if ($transaction->category->id != Category::transfer()) {
$type = $transaction->type;
$transaction->recurring()->delete();
$transaction->delete();
$message = trans('messages.success.deleted', ['type' => trans_choice('general.' . \Str::plural($type), 1)]);
return new Response($message);
} else {
$this->response->errorUnauthorized();
}
}
}
public function export($request)
{
$selected = $request->get('selected', []);
return \Excel::download(new Export($selected), trans_choice('general.revenues', 2) . '.xlsx');
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace App\BulkActions\Settings;
use App\Abstracts\BulkAction;
use App\Models\Setting\Category;
class Categories extends BulkAction
{
public $model = Category::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-settings-categories'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-settings-categories'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-settings-categories'
]
];
public function disable($request)
{
$selected = $request->get('selected', []);
$categories = $this->model::find($selected);
foreach ($categories as $category) {
if ($relationships = $this->getRelationships($category)) {
$category->enabled = 0;
$category->save();
$message = trans('messages.success.disabled', ['type' => $category->name]);
return $this->itemResponse($category->fresh(), new Transformer(), $message);
} else {
$message = trans('messages.warning.disabled', ['name' => $category->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$categories = $this->model::find($selected);
foreach ($categories as $category) {
// Can not delete the last category by type
if (Category::where('type', $category->type)->count() == 1) {
$message = trans('messages.error.last_category', ['type' => strtolower(trans_choice('general.' . $category->type . 's', 1))]);
$this->response->errorUnauthorized($message);
}
if (!$relationships = $this->getRelationships($category)) {
$category->delete();
$message = trans('messages.success.deleted', ['type' => $category->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $category->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
protected function getRelationships($category)
{
$relationships = $this->countRelationships($category, [
'items' => 'items',
'invoices' => 'invoices',
'revenues' => 'revenues',
'bills' => 'bills',
'payments' => 'payments',
]);
return $relationships;
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace App\BulkActions\Settings;
use App\Abstracts\BulkAction;
use App\Models\Setting\Currency;
class Currencies extends BulkAction
{
public $model = Currency::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-settings-currencies'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-settings-currencies'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-settings-currencies'
]
];
public function disable($request)
{
$selected = $request->get('selected', []);
$currencies = $this->model::find($selected);
foreach ($currencies as $currency) {
if (!$relationships = $this->getRelationships($currency)) {
$currency->enabled = 0;
$currency->save();
$message = trans('messages.success.disabled', ['type' => $currency->name]);
return $this->itemResponse($currency->fresh(), new Transformer(), $message);
} else {
$message = trans('messages.warning.disabled', ['name' => $currency->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$currencies = $this->model::find($selected);
foreach ($currencies as $currency) {
if (!$relationships = $this->getRelationships($currency)) {
$currency->delete();
$message = trans('messages.success.deleted', ['type' => $currency->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $currency->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
protected function getRelationships($currency)
{
$relationships = $this->countRelationships($currency, [
'accounts' => 'accounts',
'customers' => 'customers',
'invoices' => 'invoices',
'revenues' => 'revenues',
'bills' => 'bills',
'payments' => 'payments',
]);
if ($currency->code == setting('default.currency')) {
$relationships[] = strtolower(trans_choice('general.companies', 1));
}
return $relationships;
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace App\BulkActions\Settings;
use App\Abstracts\BulkAction;
use App\Models\Setting\Tax;
class Taxes extends BulkAction
{
public $model = Tax::class;
public $actions = [
'enable' => [
'name' => 'general.enable',
'message' => 'bulk_actions.message.enable',
'permission' => 'update-settings-taxes'
],
'disable' => [
'name' => 'general.disable',
'message' => 'bulk_actions.message.disable',
'permission' => 'update-settings-taxes'
],
'delete' => [
'name' => 'general.delete',
'message' => 'bulk_actions.message.deletes',
'permission' => 'delete-settings-taxes'
]
];
public function disable($request)
{
$selected = $request->get('selected', []);
$taxes = $this->model::find($selected);
foreach ($taxes as $tax) {
if (!$relationships = $this->getRelationships($tax)) {
$tax->enabled = 0;
$tax->save();
$message = trans('messages.success.disabled', ['type' => $tax->name]);
return $this->itemResponse($tax->fresh(), new Transformer(), $message);
} else {
$message = trans('messages.warning.disabled', ['name' => $tax->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
public function delete($request)
{
$this->destroy($request);
}
public function destroy($request)
{
$selected = $request->get('selected', []);
$taxes = $this->model::find($selected);
foreach ($taxes as $tax) {
if (!$relationships = $this->getRelationships($tax)) {
$tax->delete();
$message = trans('messages.success.deleted', ['type' => $tax->name]);
return new Response($message);
} else {
$message = trans('messages.warning.deleted', ['name' => $tax->name, 'text' => implode(', ', $relationships)]);
$this->response->errorUnauthorized($message);
}
}
}
protected function getRelationships($tax)
{
$relationships = $this->countRelationships($tax, [
'items' => 'items',
'invoice_items' => 'invoices',
'bill_items' => 'bills',
]);
return $relationships;
}
}

View File

@ -41,9 +41,11 @@ class BillReminder extends Command
public function handle()
{
// Get all companies
$companies = Company::all();
$companies = Company::enabled()->cursor();
foreach ($companies as $company) {
$this->info('Sending bill reminders for ' . $company->name . ' company.');
// Set company id
session(['company_id' => $company->id]);
@ -51,14 +53,14 @@ class BillReminder extends Command
Overrider::load('settings');
Overrider::load('currencies');
$company->setSettings();
// Don't send reminders if disabled
if (!$company->send_bill_reminder) {
if (!setting('schedule.send_bill_reminder')) {
$this->info('Bill reminders disabled by ' . $company->name . '.');
continue;
}
$days = explode(',', $company->schedule_bill_days);
$days = explode(',', setting('schedule.bill_days'));
foreach ($days as $day) {
$day = (int) trim($day);
@ -69,6 +71,7 @@ class BillReminder extends Command
// Unset company_id
session()->forget('company_id');
setting()->forgetAll();
}
protected function remind($day, $company)
@ -77,7 +80,7 @@ class BillReminder extends Command
$date = Date::today()->addDays($day)->toDateString();
// Get upcoming bills
$bills = Bill::with('vendor')->accrued()->notPaid()->due($date)->get();
$bills = Bill::with('contact')->accrued()->notPaid()->due($date)->cursor();
foreach ($bills as $bill) {
// Notify all users assigned to this company
@ -86,7 +89,7 @@ class BillReminder extends Command
continue;
}
$user->notify(new Notification($bill));
$user->notify(new Notification($bill, 'bill_remind_admin'));
}
}
}

View File

@ -38,10 +38,9 @@ class CompanySeed extends Command
public function handle()
{
$class = $this->laravel->make('CompanySeeder');
$seeder = $class->setContainer($this->laravel)->setCommand($this);
$seeder->__invoke();
}
}

View File

@ -212,7 +212,8 @@ class Install extends Command
}
}
private function createDatabaseTables() {
private function createDatabaseTables()
{
$this->dbHost = $this->option(self::OPT_DB_HOST);
$this->dbPort = $this->option(self::OPT_DB_PORT);
$this->dbName = $this->option(self::OPT_DB_NAME);

View File

@ -41,9 +41,11 @@ class InvoiceReminder extends Command
public function handle()
{
// Get all companies
$companies = Company::all();
$companies = Company::enabled()->cursor();
foreach ($companies as $company) {
$this->info('Sending invoice reminders for ' . $company->name . ' company.');
// Set company id
session(['company_id' => $company->id]);
@ -51,14 +53,14 @@ class InvoiceReminder extends Command
Overrider::load('settings');
Overrider::load('currencies');
$company->setSettings();
// Don't send reminders if disabled
if (!$company->send_invoice_reminder) {
if (!setting('schedule.send_invoice_reminder')) {
$this->info('Invoice reminders disabled by ' . $company->name . '.');
continue;
}
$days = explode(',', $company->schedule_invoice_days);
$days = explode(',', setting('schedule.invoice_days'));
foreach ($days as $day) {
$day = (int) trim($day);
@ -69,6 +71,7 @@ class InvoiceReminder extends Command
// Unset company_id
session()->forget('company_id');
setting()->forgetAll();
}
protected function remind($day, $company)
@ -76,13 +79,13 @@ class InvoiceReminder extends Command
// Get due date
$date = Date::today()->subDays($day)->toDateString();
// Get upcoming bills
$invoices = Invoice::with('customer')->accrued()->notPaid()->due($date)->get();
// Get upcoming invoices
$invoices = Invoice::with('contact')->accrued()->notPaid()->due($date)->cursor();
foreach ($invoices as $invoice) {
// Notify the customer
if ($invoice->customer && !empty($invoice->customer_email)) {
$invoice->customer->notify(new Notification($invoice));
if ($invoice->contact && !empty($invoice->contact_email)) {
$invoice->contact->notify(new Notification($invoice, 'invoice_remind_customer'));
}
// Notify all users assigned to this company
@ -91,7 +94,7 @@ class InvoiceReminder extends Command
continue;
}
$user->notify(new Notification($invoice));
$user->notify(new Notification($invoice, 'invoice_remind_admin'));
}
}
}

View File

@ -2,18 +2,16 @@
namespace App\Console\Commands;
use App\Events\Expense\BillCreated;
use App\Events\Expense\BillRecurring;
use App\Events\Income\InvoiceCreated;
use App\Events\Income\InvoiceRecurring;
use App\Models\Common\Company;
use App\Models\Expense\BillHistory;
use App\Models\Income\InvoiceHistory;
use App\Notifications\Expense\Bill as BillNotification;
use App\Notifications\Income\Invoice as InvoiceNotification;
use App\Traits\Incomes;
use App\Utilities\Overrider;
use Carbon\Carbon;
use Date;
use Illuminate\Console\Command;
use Recurr\Rule;
use Recurr\Transformer\ArrayTransformer;
use Recurr\Transformer\ArrayTransformerConfig;
class RecurringCheck extends Command
{
@ -32,7 +30,14 @@ class RecurringCheck extends Command
* @var string
*/
protected $description = 'Check for recurring';
/**
* The current day.
*
* @var Carbon
*/
protected $today;
/**
* Create a new command instance.
*/
@ -48,12 +53,12 @@ class RecurringCheck extends Command
*/
public function handle()
{
$this->today = Date::today();
// Get all companies
$companies = Company::all();
$companies = Company::enabled()->cursor();
foreach ($companies as $company) {
$this->info('Creating recurring records for ' . $company->name . ' company.');
// Set company id
session(['company_id' => $company->id]);
@ -61,43 +66,11 @@ class RecurringCheck extends Command
Overrider::load('settings');
Overrider::load('currencies');
$company->setSettings();
$this->today = Date::today();
foreach ($company->recurring as $recurring) {
foreach ($recurring->schedule() as $recur) {
$recur_date = Date::parse($recur->getStart()->format('Y-m-d'));
// Check if should recur today
if ($this->today->ne($recur_date)) {
continue;
}
$model = $recurring->recurable;
if (!$model) {
continue;
}
switch ($recurring->recurable_type) {
case 'App\Models\Expense\Bill':
$this->recurBill($company, $model);
break;
case 'App\Models\Income\Invoice':
$this->recurInvoice($company, $model);
break;
case 'App\Models\Expense\Payment':
case 'App\Models\Income\Revenue':
$model->cloneable_relations = [];
// Create new record
$clone = $model->duplicate();
$clone->parent_id = $model->id;
$clone->paid_at = $this->today->format('Y-m-d');
$clone->save();
break;
}
foreach ($recurring->schedule() as $schedule) {
$this->recur($recurring, $schedule);
}
}
}
@ -106,85 +79,90 @@ class RecurringCheck extends Command
session()->forget('company_id');
}
protected function recurInvoice($company, $model)
protected function recur($recurring, $schedule)
{
$model->cloneable_relations = ['items', 'totals'];
$schedule_date = Date::parse($schedule->getStart()->format('Y-m-d'));
// Create new record
$clone = $model->duplicate();
// Set original invoice id
$clone->parent_id = $model->id;
// Days between invoiced and due date
$diff_days = Date::parse($clone->due_at)->diffInDays(Date::parse($clone->invoiced_at));
// Update dates
$clone->invoiced_at = $this->today->format('Y-m-d');
$clone->due_at = $this->today->addDays($diff_days)->format('Y-m-d');
$clone->save();
// Add invoice history
InvoiceHistory::create([
'company_id' => session('company_id'),
'invoice_id' => $clone->id,
'status_code' => 'draft',
'notify' => 0,
'description' => trans('messages.success.added', ['type' => $clone->invoice_number]),
]);
// Notify the customer
if ($clone->customer && !empty($clone->customer_email)) {
$clone->customer->notify(new InvoiceNotification($clone));
// Check if should recur today
if ($this->today->ne($schedule_date)) {
return;
}
// Notify all users assigned to this company
foreach ($company->users as $user) {
if (!$user->can('read-notifications')) {
continue;
}
$user->notify(new InvoiceNotification($clone));
if (!$model = $recurring->recurable) {
return;
}
// Update next invoice number
$this->increaseNextInvoiceNumber();
switch ($recurring->recurable_type) {
case 'App\Models\Expense\Bill':
if (!$clone = $this->getDocumentClone($model, 'billed_at')) {
break;
}
event(new BillCreated($clone));
event(new BillRecurring($clone));
break;
case 'App\Models\Income\Invoice':
if (!$clone = $this->getDocumentClone($model, 'invoiced_at')) {
break;
}
event(new InvoiceCreated($clone));
event(new InvoiceRecurring($clone));
break;
case 'App\Models\Banking\Transaction':
// Skip model created on the same day, but scheduler hasn't run yet
if ($this->today->eq(Date::parse($model->paid_at->format('Y-m-d')))) {
break;
}
$model->cloneable_relations = [];
// Create new record
$clone = $model->duplicate();
$clone->parent_id = $model->id;
$clone->paid_at = $this->today->format('Y-m-d');
$clone->save();
break;
}
}
protected function recurBill($company, $model)
/**
* Clone the document and return it.
*
* @param $model
* @param $date_field
*
* @return boolean|object
*/
protected function getDocumentClone($model, $date_field)
{
// Skip model created on the same day, but scheduler hasn't run yet
if ($this->today->eq(Date::parse($model->$date_field->format('Y-m-d')))) {
return false;
}
$model->cloneable_relations = ['items', 'totals'];
// Create new record
$clone = $model->duplicate();
// Set original bill id
// Set original model id
$clone->parent_id = $model->id;
// Days between invoiced and due date
$diff_days = Date::parse($clone->due_at)->diffInDays(Date::parse($clone->invoiced_at));
// Days between issued and due date
$diff_days = Date::parse($clone->due_at)->diffInDays(Date::parse($clone->$date_field));
// Update dates
$clone->billed_at = $this->today->format('Y-m-d');
$clone->due_at = $this->today->addDays($diff_days)->format('Y-m-d');
$clone->$date_field = $this->today->format('Y-m-d');
$clone->due_at = $this->today->copy()->addDays($diff_days)->format('Y-m-d');
$clone->save();
// Add bill history
BillHistory::create([
'company_id' => session('company_id'),
'bill_id' => $clone->id,
'status_code' => 'draft',
'notify' => 0,
'description' => trans('messages.success.added', ['type' => $clone->bill_number]),
]);
// Notify all users assigned to this company
foreach ($company->users as $user) {
if (!$user->can('read-notifications')) {
continue;
}
$user->notify(new BillNotification($clone));
}
return $clone;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class UserSeed extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:seed {user} {company}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Seed for specific user';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$class = $this->laravel->make('UserSeeder');
$seeder = $class->setContainer($this->laravel)->setCommand($this);
$seeder->__invoke();
}
}

View File

@ -12,17 +12,7 @@ class Kernel extends ConsoleKernel
*
* @var array
*/
protected $commands = [
Commands\BillReminder::class,
Commands\CompanySeed::class,
Commands\Install::class,
Commands\InvoiceReminder::class,
Commands\ModuleDelete::class,
Commands\ModuleDisable::class,
Commands\ModuleEnable::class,
Commands\ModuleInstall::class,
Commands\RecurringCheck::class,
];
protected $commands = [];
/**
* Define the application's command schedule.
@ -52,5 +42,7 @@ class Kernel extends ConsoleKernel
protected function commands()
{
require base_path('routes/console.php');
$this->load(__DIR__ . '/Commands');
}
}

View File

@ -37,7 +37,7 @@ class $CLASS$ extends Command
*
* @return mixed
*/
public function fire()
public function handle()
{
//
}

View File

@ -11,5 +11,15 @@
"psr-4": {
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\": ""
}
},
"extra": {
"laravel": {
"providers": [
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Providers\\Main"
],
"aliases": {
}
}
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace $CLASS_NAMESPACE$;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
class $CLASS$ extends Controller
{
/**
* Display a listing of the resource.
* @return Response
*/
public function index()
{
//
}
/**
* Store a newly created resource in storage.
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
//
}
/**
* Show the specified resource.
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
/**
* Update the specified resource in storage.
* @param Request $request
* @param int $id
* @return Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
* @param int $id
* @return Response
*/
public function destroy($id)
{
//
}
}

View File

@ -14,7 +14,7 @@ class $CLASS$ extends Controller
*/
public function index()
{
return view('$LOWER_NAME$::index');
return view('$ALIAS$::index');
}
/**
@ -23,50 +23,57 @@ class $CLASS$ extends Controller
*/
public function create()
{
return view('$LOWER_NAME$::create');
return view('$ALIAS$::create');
}
/**
* Store a newly created resource in storage.
* @param Request $request
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
//
}
/**
* Show the specified resource.
* @param int $id
* @return Response
*/
public function show()
public function show($id)
{
return view('$LOWER_NAME$::show');
return view('$ALIAS$::show');
}
/**
* Show the form for editing the specified resource.
* @param int $id
* @return Response
*/
public function edit()
public function edit($id)
{
return view('$LOWER_NAME$::edit');
return view('$ALIAS$::edit');
}
/**
* Update the specified resource in storage.
* @param Request $request
* @param Request $request
* @param int $id
* @return Response
*/
public function update(Request $request)
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
* @param int $id
* @return Response
*/
public function destroy()
public function destroy($id)
{
//
}
}

View File

@ -0,0 +1,9 @@
<?php
use Faker\Generator as Faker;
$factory->define(Model::class, function (Faker $faker) {
return [
//
];
});

View File

@ -0,0 +1,34 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class $CLASS$ implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}

View File

@ -2,14 +2,12 @@
namespace $NAMESPACE$;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Foundation\Bus\Dispatchable;
class $CLASS$ implements ShouldQueue
{
use InteractsWithQueue, SerializesModels, Queueable;
use Dispatchable, Queueable;
/**
* Create a new job instance.

View File

@ -1,19 +1,14 @@
{
"name": "$STUDLY_NAME$",
"alias": "$LOWER_NAME$",
"description": "",
"alias": "$ALIAS$",
"version": "1.0.0",
"category": "payment-gateway",
"keywords": [],
"category": "accounting",
"active": 1,
"order": 0,
"providers": [
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Providers\\$STUDLY_NAME$ServiceProvider"
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Providers\\Main"
],
"aliases": {},
"files": [
"start.php"
],
"files": [],
"requires": [],
"settings": []
}

View File

@ -0,0 +1,8 @@
<?php
return [
'name' => '$STUDLY_NAME$',
'description' => 'This is my awesome module',
];

View File

@ -0,0 +1,30 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class $CLASS$
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
//
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class $CLASS$ implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
//
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace $NAMESPACE$;
use $EVENTNAME$;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class $CLASS$ implements ShouldQueue
{
use InteractsWithQueue;
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param $SHORTEVENTNAME$ $event
* @return void
*/
public function handle($SHORTEVENTNAME$ $event)
{
//
}
}

View File

@ -21,10 +21,10 @@ class $CLASS$
/**
* Handle the event.
*
* @param \$EVENTNAME$ $event
* @param $SHORTEVENTNAME$ $event
* @return void
*/
public function handle(\$EVENTNAME$ $event)
public function handle($SHORTEVENTNAME$ $event)
{
//
}

View File

@ -14,7 +14,7 @@ class $CLASS$ extends Migration
public function up()
{
Schema::create('$TABLE$', function (Blueprint $table) {
$table->increments('id');
$table->bigIncrements('id');
$FIELDS$
$table->timestamps();
});

View File

@ -24,7 +24,7 @@ class $CLASS$ extends Migration
public function down()
{
Schema::create('$TABLE$', function (Blueprint $table) {
$table->increments('id');
$table->bigIncrements('id');
$FIELDS$
$table->timestamps();
});

View File

@ -0,0 +1,17 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"cross-env": "^5.1.4",
"laravel-mix": "^4.0.7",
"laravel-mix-merge-manifest": "^0.1.2"
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Auth\Access\HandlesAuthorization;
class $CLASS$
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* @return void
*/
public function __construct()
{
//
}
}

View File

@ -6,13 +6,6 @@ use Illuminate\Support\ServiceProvider;
class $CLASS$ extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*

View File

@ -0,0 +1,19 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Http\Resources\Json\ResourceCollection;
class $CLASS$ extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Http\Resources\Json\Resource;
class $CLASS$ extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@ -1,41 +0,0 @@
<?php
namespace $MODULE_NAMESPACE$\$MODULE$\Providers;
use Illuminate\Routing\Router;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class $NAME$ extends ServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
*
* @var string
*/
protected $rootUrlNamespace = '$MODULE_NAMESPACE$\$MODULE$\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @param Router $router
* @return void
*/
public function before(Router $router)
{
//
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map(Router $router)
{
// if (!app()->routesAreCached()) {
// require __DIR__ . '/Http/routes.php';
// }
}
}

View File

@ -1,6 +0,0 @@
<?php
Route::group(['middleware' => 'web', 'prefix' => '$LOWER_NAME$', 'namespace' => '$MODULE_NAMESPACE$\$STUDLY_NAME$\Http\Controllers'], function()
{
Route::get('/', '$STUDLY_NAME$Controller@index');
});

View File

@ -0,0 +1,10 @@
<?php
Route::group([
'middleware' => 'admin',
'namespace' => 'Modules\$STUDLY_NAME$\Http\Controllers'
], function () {
Route::prefix('$ALIAS$')->group(function() {
// Route::get('/', 'Main@index');
});
});

View File

@ -0,0 +1,10 @@
<?php
Route::group([
'prefix' => 'portal',
'middleware' => 'portal',
'namespace' => 'Modules\$STUDLY_NAME$\Http\Controllers'
], function () {
// Route::get('invoices/{invoice}/$ALIAS$', 'Main@show')->name('portal.invoices.$ALIAS$.show');
// Route::post('invoices/{invoice}/$ALIAS$/confirm', 'Main@confirm')->name('portal.invoices.$ALIAS$.confirm');
});

View File

@ -0,0 +1,40 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Contracts\Validation\Rule;
class $CLASS$ implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
//
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'The validation error message.';
}
}

View File

@ -2,18 +2,10 @@
namespace $NAMESPACE$;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
use Illuminate\Support\ServiceProvider as Provider;
class $CLASS$ extends ServiceProvider
class $NAME$ extends Provider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Boot the application events.
*
@ -21,10 +13,9 @@ class $CLASS$ extends ServiceProvider
*/
public function boot()
{
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->registerFactories();
$this->loadTranslations();
$this->loadViews();
$this->loadMigrations();
}
/**
@ -34,68 +25,57 @@ class $CLASS$ extends ServiceProvider
*/
public function register()
{
//
$this->loadRoutes();
}
/**
* Register config.
* Load views.
*
* @return void
*/
protected function registerConfig()
public function loadViews()
{
$this->publishes([
__DIR__.'/../$PATH_CONFIG$/config.php' => config_path('$LOWER_NAME$.php'),
], 'config');
$this->mergeConfigFrom(
__DIR__.'/../$PATH_CONFIG$/config.php', '$LOWER_NAME$'
);
$this->loadViewsFrom(__DIR__ . '/../Resources/views', '$ALIAS$');
}
/**
* Register views.
* Load translations.
*
* @return void
*/
public function registerViews()
public function loadTranslations()
{
$viewPath = resource_path('views/modules/$LOWER_NAME$');
$sourcePath = __DIR__.'/../$PATH_VIEWS$';
$this->publishes([
$sourcePath => $viewPath
]);
$this->loadViewsFrom(array_merge(array_map(function ($path) {
return $path . '/modules/$LOWER_NAME$';
}, \Config::get('view.paths')), [$sourcePath]), '$LOWER_NAME$');
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', '$ALIAS$');
}
/**
* Register translations.
* Load migrations.
*
* @return void
*/
public function registerTranslations()
public function loadMigrations()
{
$langPath = resource_path('lang/modules/$LOWER_NAME$');
$this->loadMigrationsFrom(__DIR__ . '/../$MIGRATIONS_PATH$');
}
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, '$LOWER_NAME$');
} else {
$this->loadTranslationsFrom(__DIR__ .'/../$PATH_LANG$', '$LOWER_NAME$');
/**
* Load routes.
*
* @return void
*/
public function loadRoutes()
{
if (app()->routesAreCached()) {
return;
}
}
/**
* Register an additional directory of factories.
* @source https://github.com/sebastiaanluca/laravel-resource-flow/blob/develop/src/Modules/ModuleServiceProvider.php#L66
*/
public function registerFactories()
{
if (! app()->environment('production')) {
app(Factory::class)->load(__DIR__ . '/Database/factories');
$routes = [
'admin.php',
'portal.php',
];
foreach ($routes as $route) {
$this->loadRoutesFrom(__DIR__ . '/../$ROUTES_PATH$/' . $route);
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace $NAMESPACE$\Database\Seeders;
namespace $NAMESPACE$;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;

View File

@ -1,17 +0,0 @@
<?php
/*
|--------------------------------------------------------------------------
| Register Namespaces And Routes
|--------------------------------------------------------------------------
|
| When a module starting, this file will executed automatically. This helps
| to register some namespaces like translator or view. Also this file
| will load the routes file for each module. You may also modify
| this file as you want.
|
*/
if (!app()->routesAreCached()) {
require __DIR__ . '/Http/routes.php';
}

View File

@ -0,0 +1,19 @@
<?php
namespace $NAMESPACE$;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class $CLASS$ extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testExample()
{
$this->assertTrue(true);
}
}

View File

@ -1,9 +1,9 @@
@extends('$LOWER_NAME$::layouts.master')
@extends('layouts.admin')
@section('content')
<h1>Hello World</h1>
<p>
This view is loaded from module: {!! config('$LOWER_NAME$.name') !!}
This view is loaded from module: {!! config('$ALIAS$.name') !!}
</p>
@stop

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Module $STUDLY_NAME$</title>
</head>
<body>
@yield('content')
</body>
</html>

View File

@ -0,0 +1,14 @@
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('Resources/assets/js/$ALIAS$.js', 'Assets/js/');

View File

@ -0,0 +1,26 @@
<?php
namespace App\Events\Common;
use Illuminate\Queue\SerializesModels;
class CompanySwitched
{
use SerializesModels;
public $company;
public $old_company_id;
/**
* Create a new event instance.
*
* @param $company
* @param $old_company_id
*/
public function __construct($company, $old_company_id)
{
$this->company = $company;
$this->old_company_id = $old_company_id;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Events\Common;
use Illuminate\Queue\SerializesModels;
class ReportFilterApplying
{
use SerializesModels;
public $class;
public $model;
public $args;
/**
* Create a new event instance.
*
* @param $class
* @param $model
* @param $args
*/
public function __construct($class, $model, $args)
{
$this->class = $class;
$this->model = $model;
$this->args = $args;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Events\Common;
use Illuminate\Queue\SerializesModels;
class ReportFilterShowing
{
use SerializesModels;
public $class;
/**
* Create a new event instance.
*
* @param $class
*/
public function __construct($class)
{
$this->class = $class;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Events\Common;
use Illuminate\Queue\SerializesModels;
class ReportGroupApplying
{
use SerializesModels;
public $class;
public $model;
public $args;
/**
* Create a new event instance.
*
* @param $class
* @param $model
* @param $args
*/
public function __construct($class, $model, $args)
{
$this->class = $class;
$this->model = $model;
$this->args = $args;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Events\Common;
use Illuminate\Queue\SerializesModels;
class ReportGroupShowing
{
use SerializesModels;
public $class;
/**
* Create a new event instance.
*
* @param $class
*/
public function __construct($class)
{
$this->class = $class;
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace App\Events;
class CompanySwitched
{
public $company;
/**
* Create a new event instance.
*
* @param $company
*/
public function __construct($company)
{
$this->company = $company;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Expense;
use Illuminate\Queue\SerializesModels;
class BillCreated
{
use SerializesModels;
public $bill;
/**

View File

@ -0,0 +1,22 @@
<?php
namespace App\Events\Expense;
use Illuminate\Queue\SerializesModels;
class BillRecurring
{
use SerializesModels;
public $bill;
/**
* Create a new event instance.
*
* @param $bill
*/
public function __construct($bill)
{
$this->bill = $bill;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Expense;
use Illuminate\Queue\SerializesModels;
class BillUpdated
{
use SerializesModels;
public $bill;
/**

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoiceCreated
{
use SerializesModels;
public $invoice;
/**

View File

@ -0,0 +1,22 @@
<?php
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoiceCreating
{
use SerializesModels;
public $request;
/**
* Create a new event instance.
*
* @param $request
*/
public function __construct($request)
{
$this->request = $request;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoicePrinting
{
use SerializesModels;
public $invoice;
/**

View File

@ -0,0 +1,22 @@
<?php
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoiceRecurring
{
use SerializesModels;
public $invoice;
/**
* Create a new event instance.
*
* @param $invoice
*/
public function __construct($invoice)
{
$this->invoice = $invoice;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Income;
class InvoiceUpdated
use Illuminate\Queue\SerializesModels;
class InvoiceSent
{
use SerializesModels;
public $invoice;
/**

View File

@ -0,0 +1,26 @@
<?php
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoiceUpdated
{
use SerializesModels;
public $invoice;
public $request;
/**
* Create a new event instance.
*
* @param $invoice
* @param $request
*/
public function __construct($invoice, $request)
{
$this->invoice = $invoice;
$this->request = $request;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoiceUpdating
{
use SerializesModels;
public $invoice;
public $request;
/**
* Create a new event instance.
*
* @param $invoice
* @param $request
*/
public function __construct($invoice, $request)
{
$this->invoice = $invoice;
$this->request = $request;
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Events\Income;
use Illuminate\Queue\SerializesModels;
class InvoiceViewed
{
use SerializesModels;
public $invoice;
/**
* Create a new event instance.
*
* @param $invoice
*/
public function __construct($invoice)
{
$this->invoice = $invoice;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Income;
class InvoicePaid
use Illuminate\Queue\SerializesModels;
class PaymentReceived
{
use SerializesModels;
public $invoice;
public $request;

View File

@ -0,0 +1,30 @@
<?php
namespace App\Events\Install;
use Illuminate\Queue\SerializesModels;
class UpdateCopied
{
use SerializesModels;
public $alias;
public $old;
public $new;
/**
* Create a new event instance.
*
* @param $alias
* @param $old
* @param $new
*/
public function __construct($alias, $old, $new)
{
$this->alias = $alias;
$this->old = $old;
$this->new = $new;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Events\Install;
use Illuminate\Queue\SerializesModels;
class UpdateDownloaded
{
use SerializesModels;
public $alias;
public $old;
public $new;
/**
* Create a new event instance.
*
* @param $alias
* @param $old
* @param $new
*/
public function __construct($alias, $old, $new)
{
$this->alias = $alias;
$this->old = $old;
$this->new = $new;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Install;
use Illuminate\Queue\SerializesModels;
class UpdateFinished
{
use SerializesModels;
public $alias;
public $old;

View File

@ -0,0 +1,30 @@
<?php
namespace App\Events\Install;
use Illuminate\Queue\SerializesModels;
class UpdateUnzipped
{
use SerializesModels;
public $alias;
public $old;
public $new;
/**
* Create a new event instance.
*
* @param $alias
* @param $old
* @param $new
*/
public function __construct($alias, $old, $new)
{
$this->alias = $alias;
$this->old = $old;
$this->new = $new;
}
}

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Menu;
class CustomerMenuCreated
use Illuminate\Queue\SerializesModels;
class AdminCreated
{
use SerializesModels;
public $menu;
/**

View File

@ -1,9 +1,13 @@
<?php
namespace App\Events;
namespace App\Events\Menu;
class AdminMenuCreated
use Illuminate\Queue\SerializesModels;
class AdminCreating
{
use SerializesModels;
public $menu;
/**

Some files were not shown because too many files have changed in this diff Show More