akaunting 3.0 (the last dance)

This commit is contained in:
Burak Civan
2022-06-01 10:15:55 +03:00
parent cead09f6d4
commit d9c0764572
3812 changed files with 126831 additions and 102949 deletions

View File

@@ -28,10 +28,6 @@ class AddLandingPages
'permission' => 'read-sales-invoices',
'translate' => trans_choice('general.invoices', 2),
],
'revenues.index' => [
'permission' => 'read-sales-revenues',
'translate' => trans_choice('general.revenues', 2),
],
'customers.index' => [
'permission' => 'read-sales-customers',
'translate' => trans_choice('general.customers', 2),
@@ -40,10 +36,6 @@ class AddLandingPages
'permission' => 'read-purchases-bills',
'translate' => trans_choice('general.bills', 2),
],
'payments.index' => [
'permission' => 'read-purchases-payments',
'translate' => trans_choice('general.payments', 2),
],
'vendors.index' => [
'permission' => 'read-purchases-vendors',
'translate' => trans_choice('general.vendors', 2),
@@ -68,10 +60,6 @@ class AddLandingPages
'permission' => 'read-common-reports',
'translate' => trans_choice('general.reports', 2),
],
'settings.index' => [
'permission' => 'read-settings-settings',
'translate' => trans_choice('general.settings', 2),
],
'categories.index' => [
'permission' => 'read-settings-categories',
'translate' => trans_choice('general.categories', 2),

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Listeners\Auth;
use App\Events\Auth\UserDeleted as Event;
use App\Jobs\Auth\DeleteInvitation;
use App\Models\Auth\UserInvitation;
use App\Traits\Jobs;
class DeleteUserInvitation
{
use Jobs;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$invitations = UserInvitation::where('user_id', $event->user->id)->get();
foreach ($invitations as $invitation) {
$this->dispatch(new DeleteInvitation($invitation));
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Listeners\Auth;
use App\Events\Auth\InvitationCreated as Event;
use App\Notifications\Auth\Invitation as Notification;
class SendUserInvitation
{
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$invitation = $event->invitation;
$invitation->user->notify(new Notification($invitation));
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Listeners\Banking;
use App\Events\Banking\TransactionCreated as Event;
use App\Traits\Transactions;
class IncreaseNextTransactionNumber
{
use Transactions;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$suffix = $event->transaction->isRecurringTransaction() ? '-recurring' : '';
// Update next transaction number
$this->increaseNextTransactionNumber($suffix);
}
}

View File

@@ -23,11 +23,11 @@ class MarkDocumentCancelled
$type_text = '';
if ($alias = config('type.' . $event->document->type . '.alias', '')) {
if ($alias = config('type.document.' . $event->document->type . '.alias', '')) {
$type_text .= $alias . '::';
}
$type_text .= 'general.' . config('type.' . $event->document->type .'.translation.prefix');
$type_text .= 'general.' . config('type.document.' . $event->document->type .'.translation.prefix');
$type = trans_choice($type_text, 1);

View File

@@ -26,11 +26,11 @@ class MarkDocumentReceived
$type_text = '';
if ($alias = config('type.' . $event->document->type . '.alias', '')) {
if ($alias = config('type.document.' . $event->document->type . '.alias', '')) {
$type_text .= $alias . '::';
}
$type_text .= 'general.' . config('type.' . $event->document->type .'.translation.prefix');
$type_text .= 'general.' . config('type.document.' . $event->document->type .'.translation.prefix');
$type = trans_choice($type_text, 1);

View File

@@ -26,11 +26,11 @@ class MarkDocumentSent
$type_text = '';
if ($alias = config('type.' . $event->document->type . '.alias', '')) {
if ($alias = config('type.document.' . $event->document->type . '.alias', '')) {
$type_text .= $alias . '::';
}
$type_text .= 'general.' . config('type.' . $event->document->type .'.translation.prefix');
$type_text .= 'general.' . config('type.document.' . $event->document->type .'.translation.prefix');
$type = trans_choice($type_text, 1);

View File

@@ -31,11 +31,11 @@ class MarkDocumentViewed
$type_text = '';
if ($alias = config('type.' . $event->document->type . '.alias', '')) {
if ($alias = config('type.document.' . $event->document->type . '.alias', '')) {
$type_text .= $alias . '::';
}
$type_text .= 'general.' . config('type.' . $event->document->type .'.translation.prefix');
$type_text .= 'general.' . config('type.document.' . $event->document->type .'.translation.prefix');
$type = trans_choice($type_text, 1);

View File

@@ -19,10 +19,6 @@ class SendDocumentPaymentNotification
return;
}
if (!empty($event->request['mark_paid'])) {
return;
}
$document = $event->document;
$transaction = $document->transactions()->latest()->first();
@@ -33,7 +29,7 @@ class SendDocumentPaymentNotification
// Notify all users assigned to this company
foreach ($document->company->users as $user) {
if (!$user->can('read-notifications')) {
if ($user->cannot('read-notifications')) {
continue;
}

View File

@@ -3,9 +3,12 @@
namespace App\Listeners\Document;
use App\Events\Document\DocumentRecurring as Event;
use App\Traits\Documents;
class SendDocumentRecurringNotification
{
use Documents;
/**
* Handle the event.
*
@@ -15,21 +18,25 @@ class SendDocumentRecurringNotification
public function handle(Event $event)
{
$document = $event->document;
$config = config('type.' . $document->type . '.notification');
$config = config('type.document.' . $document->type . '.notification');
if (empty($config) || empty($config['class'])) {
return;
}
if ($document->parent?->recurring?->auto_send == false) {
return;
}
$notification = $config['class'];
// Notify the customer
if ($config['notify_contact'] && $document->contact && !empty($document->contact_email)) {
if ($this->canNotifyTheContactOfDocument($document)) {
$document->contact->notify(new $notification($document, "{$document->type}_recur_customer"));
}
// Check if should notify users
if (!$config['notify_user']) {
if (! $config['notify_user']) {
return;
}

View File

@@ -3,9 +3,12 @@
namespace App\Listeners\Document;
use App\Events\Document\DocumentReminded as Event;
use App\Traits\Documents;
class SendDocumentReminderNotification
{
use Documents;
/**
* Handle the event.
*
@@ -18,13 +21,13 @@ class SendDocumentReminderNotification
$notification = $event->notification;
// Notify the customer
if ($document->contact && !empty($document->contact_email)) {
if ($this->canNotifyTheContactOfDocument($document)) {
$document->contact->notify(new $notification($document, "{$document->type}_remind_customer"));
}
// Notify all users assigned to this company
foreach ($document->company->users as $user) {
if (!$user->can('read-notifications')) {
if ($user->cannot('read-notifications')) {
continue;
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Listeners\Document;
use App\Events\Document\DocumentViewed as Event;
use App\Traits\Documents;
class SendDocumentViewNotification
{
use Documents;
/**
* Handle the event.
*
* @param $event
* @return array
*/
public function handle(Event $event)
{
$document = $event->document;
$config = config('type.document.' . $document->type . '.notification');
if (empty($config) || empty($config['class'])) {
return;
}
// Check if should notify users
if (! $config['notify_user']) {
return;
}
$notification = $config['class'];
// Notify all users assigned to this company
foreach ($document->company->users as $user) {
if ($user->cannot('read-notifications')) {
continue;
}
$user->notify(new $notification($document, "{$document->type}_view_admin"));
}
}
}

View File

@@ -53,7 +53,7 @@ class SettingFieldCreated
$company = Company::find($document->company_id);
foreach ($files as $key => $value) {
// Upload attachment
// Upload attachment
$media = $this->getMedia($value, 'settings');
$company->attachMedia($media, Str::snake($real_key));

View File

@@ -2,10 +2,12 @@
namespace App\Listeners\Menu;
use App\Events\Menu\AdminCreated as Event;
use App\Traits\Permissions;
use App\Events\Menu\AdminCreated as Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
class AddAdminItems
class ShowInAdmin
{
use Permissions;
@@ -22,85 +24,53 @@ class AddAdminItems
$attr = ['icon' => ''];
// Dashboards
$title = trim(trans_choice('general.dashboards', 2));
$title = trim(trans_choice('general.dashboards', 1));
if ($this->canAccessMenuItem($title, 'read-common-dashboards')) {
$dashboards = user()->dashboards()->enabled()->get();
if ($dashboards->count() > 1) {
$menu->dropdown($title, function ($sub) use ($attr, $dashboards) {
foreach ($dashboards as $key => $dashboard) {
if (session('dashboard_id') != $dashboard->id) {
$sub->route('dashboards.switch', $dashboard->name, ['dashboard' => $dashboard->id], $key, $attr);
} else {
$sub->url('/' . company_id(), $dashboard->name, $key, $attr);
}
}
}, 10, [
'url' => '/' . company_id(),
'title' => $title,
'icon' => 'fa fa-tachometer-alt',
]);
} else {
$menu->add([
'url' => '/' . company_id(),
'title' => trans_choice('general.dashboards', 1),
'icon' => 'fa fa-tachometer-alt',
'order' => 10,
]);
}
$inactive = ('dashboard' != Route::currentRouteName()) ? true : false;
$menu->route('dashboard', $title, [], 10, ['icon' => 'speed', 'inactive' => $inactive]);
}
// Items
$title = trim(trans_choice('general.items', 2));
if ($this->canAccessMenuItem($title, 'read-common-items')) {
$menu->route('items.index', $title, [], 20, ['icon' => 'fa fa-cube']);
$menu->route('items.index', $title, [], 20, ['icon' => 'inventory_2']);
}
// Sales
$title = trim(trans_choice('general.sales', 2));
if ($this->canAccessMenuItem($title, ['read-sales-invoices', 'read-sales-revenues', 'read-sales-customers'])) {
if ($this->canAccessMenuItem($title, ['read-sales-invoices', 'read-sales-customers'])) {
$menu->dropdown($title, function ($sub) use ($attr) {
$title = trim(trans_choice('general.invoices', 2));
if ($this->canAccessMenuItem($title, 'read-sales-invoices')) {
$sub->route('invoices.index', $title, [], 10, $attr);
}
$title = trim(trans_choice('general.revenues', 2));
if ($this->canAccessMenuItem($title, 'read-sales-revenues')) {
$sub->route('revenues.index', $title, [], 20, $attr);
}
$title = trim(trans_choice('general.customers', 2));
if ($this->canAccessMenuItem($title, 'read-sales-customers')) {
$sub->route('customers.index', $title, [], 30, $attr);
$sub->route('customers.index', $title, [], 20, $attr);
}
}, 30, [
'title' => $title,
'icon' => 'fa fa-money-bill',
'icon' => 'payments',
]);
}
// Purchases
$title = trim(trans_choice('general.purchases', 2));
if ($this->canAccessMenuItem($title, ['read-purchases-bills', 'read-purchases-payments', 'read-purchases-vendors'])) {
if ($this->canAccessMenuItem($title, ['read-purchases-bills', 'read-purchases-vendors'])) {
$menu->dropdown($title, function ($sub) use ($attr) {
$title = trim(trans_choice('general.bills', 2));
if ($this->canAccessMenuItem($title, 'read-purchases-bills')) {
$sub->route('bills.index', $title, [], 10, $attr);
}
$title = trim(trans_choice('general.payments', 2));
if ($this->canAccessMenuItem($title, 'read-purchases-payments')) {
$sub->route('payments.index', $title, [], 20, $attr);
}
$title = trim(trans_choice('general.vendors', 2));
if ($this->canAccessMenuItem($title, 'read-purchases-vendors')) {
$sub->route('vendors.index', $title, [], 30, $attr);
$sub->route('vendors.index', $title, [], 20, $attr);
}
}, 40, [
'title' => $title,
'icon' => 'fa fa-shopping-cart',
'icon' => 'shopping_cart',
]);
}
@@ -113,14 +83,14 @@ class AddAdminItems
$sub->route('accounts.index', $title, [], 10, $attr);
}
$title = trim(trans_choice('general.transfers', 2));
if ($this->canAccessMenuItem($title, 'read-banking-transfers')) {
$sub->route('transfers.index', $title, [], 20, $attr);
}
$title = trim(trans_choice('general.transactions', 2));
if ($this->canAccessMenuItem($title, 'read-banking-transactions')) {
$sub->route('transactions.index', $title, [], 30, $attr);
$sub->route('transactions.index', $title, [], 20, $attr);
}
$title = trim(trans_choice('general.transfers', 2));
if ($this->canAccessMenuItem($title, 'read-banking-transfers')) {
$sub->route('transfers.index', $title, [], 30, $attr);
}
$title = trim(trans_choice('general.reconciliations', 2));
@@ -129,26 +99,21 @@ class AddAdminItems
}
}, 50, [
'title' => $title,
'icon' => 'fa fa-briefcase',
'icon' => 'account_balance',
]);
}
// Reports
$title = trim(trans_choice('general.reports', 2));
if ($this->canAccessMenuItem($title, 'read-common-reports')) {
$menu->route('reports.index', $title, [], 60, ['icon' => 'fa fa-chart-pie']);
}
// Settings
$title = trim(trans_choice('general.settings', 2));
if ($this->canAccessMenuItem($title, 'read-settings-settings')) {
$menu->route('settings.index', $title, [], 70, ['icon' => 'fa fa-cog']);
$menu->route('reports.index', $title, [], 60, ['icon' => 'donut_small']);
}
// Apps
$title = trim(trans_choice('general.modules', 2));
if ($this->canAccessMenuItem($title, 'read-modules-home')) {
$menu->route('apps.home.index', $title, [], 80, ['icon' => 'fa fa-rocket']);
$active = (Str::contains(Route::currentRouteName(), 'apps')) ? true : false;
$menu->route('apps.home.index', $title, [], 80, ['icon' => 'rocket_launch', 'active' => $active]);
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Listeners\Menu;
use App\Events\Menu\NewwCreated as Event;
use App\Traits\Permissions;
class ShowInNeww
{
use Permissions;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$menu = $event->menu;
$title = trim(trans_choice('general.invoices', 1));
if ($this->canAccessMenuItem($title, 'create-sales-invoices')) {
$menu->route('invoices.create', $title, [], 10, ['icon' => 'description']);
}
$title = trim(trans_choice('general.incomes', 1));
if ($this->canAccessMenuItem($title, 'create-banking-transactions')) {
$menu->route('transactions.create', $title, ['type' => 'income'], 20, ['icon' => 'request_quote']);
}
$title = trim(trans_choice('general.customers', 1));
if ($this->canAccessMenuItem($title, 'create-sales-customers')) {
$menu->route('customers.create', $title, [], 30, ['icon' => 'person']);
}
$title = trim(trans_choice('general.bills', 1));
if ($this->canAccessMenuItem($title, 'create-purchases-bills')) {
$menu->route('bills.create', $title, [], 40, ['icon' => 'file_open']);
}
$title = trim(trans_choice('general.expenses', 1));
if ($this->canAccessMenuItem($title, 'create-banking-transactions')) {
$menu->route('transactions.create', $title, ['type' => 'expense'], 50, ['icon' => 'paid']);
}
$title = trim(trans_choice('general.vendors', 1));
if ($this->canAccessMenuItem($title, 'create-purchases-vendors')) {
$menu->route('vendors.create', $title, [], 60, ['icon' => 'engineering']);
}
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Listeners\Menu;
use App\Events\Menu\NotificationsCreated as Event;
use App\Traits\Modules;
use App\Utilities\Versions;
use Illuminate\Notifications\DatabaseNotification;
class ShowInNotifications
{
use Modules;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if (user()->cannot('read-notifications')) {
return;
}
// Notification tables
$notifications = collect();
// Update notifications
if (user()->can('read-install-updates')) {
$updates = Versions::getUpdates();
foreach ($updates as $key => $update) {
$prefix = ($key == 'core') ? 'core' : 'module';
$new = new DatabaseNotification();
$new->id = $key;
$new->type = 'updates';
$new->notifiable_type = "users";
$new->notifiable_id = user()->id;
$new->data = [
'title' => $key . ' (v' . $update . ')',
'description' => '<a href="' . route('updates.index') . '">' . trans('install.update.' . $prefix) . '</a>',
];
$new->created_at = \Carbon\Carbon::now();
$notifications->push($new);
}
}
// New app notifcations
$new_apps = $this->getNotifications('new-apps');
foreach ($new_apps as $key => $new_app) {
if (setting('notifications.' . user()->id . '.' . $new_app->alias)) {
unset($new_apps[$key]);
continue;
}
$new = new DatabaseNotification();
$new->id = $key;
$new->type = 'new-apps';
$new->notifiable_type = "users";
$new->notifiable_id = user()->id;
$new->data = [
'title' => $new_app->name,
'description' => $new_app->alias,
];
$new->created_at = $new_app->started_at->date;
$notifications->push($new);
}
$unReadNotifications = user()->unReadNotifications;
foreach ($unReadNotifications as $unReadNotification) {
$notifications->push($unReadNotification);
}
$event->notifications->notifications = $notifications;
}
}

View File

@@ -4,7 +4,7 @@ namespace App\Listeners\Menu;
use App\Events\Menu\PortalCreated as Event;
class AddPortalItems
class ShowInPortal
{
/**
* Handle the event.
@@ -23,14 +23,14 @@ class AddPortalItems
$inactive = true;
}
$menu->route('portal.dashboard', trans_choice('general.dashboards', 1), [], 10, ['icon' => 'fa fa-tachometer-alt', 'inactive' => $inactive]);
$menu->route('portal.dashboard', trans_choice('general.dashboards', 1), [], 10, ['icon' => 'speed', 'inactive' => $inactive]);
if ($user->can('read-portal-invoices')) {
$menu->route('portal.invoices.index', trans_choice('general.invoices', 2), [], 20, ['icon' => 'fa fa-file-signature']);
$menu->route('portal.invoices.index', trans_choice('general.invoices', 2), [], 20, ['icon' => 'description']);
}
if ($user->can('read-portal-payments')) {
$menu->route('portal.payments.index', trans_choice('general.payments', 2), [], 30, ['icon' => 'fa fa-money-bill']);
$menu->route('portal.payments.index', trans_choice('general.payments', 2), [], 30, ['icon' => 'credit_score']);
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Listeners\Menu;
use App\Events\Menu\ProfileCreated as Event;
use App\Traits\Permissions;
class ShowInProfile
{
use Permissions;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$menu = $event->menu;
$title = trim(trans('auth.profile'));
if ($this->canAccessMenuItem($title, 'read-auth-profile')) {
$menu->route('profile.edit', $title, [user()->id], 10, ['icon' => 'badge']);
}
if (user()->isCustomer()) {
$menu->route('portal.profile.edit', $title, [user()->id], 10, ['icon' => 'badge']);
}
$title = trim(trans_choice('general.users', 2));
if ($this->canAccessMenuItem($title, 'read-auth-users')) {
$menu->route('users.index', $title, [], 20, ['icon' => 'people']);
}
$title = trim(trans('auth.logout'));
$menu->route('logout', $title, [], 90, ['icon' => 'power_settings_new', 'class' => 'mt-5']);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Listeners\Menu;
use App\Events\Menu\SettingsCreated as Event;
use App\Traits\Permissions;
class ShowInSettings
{
use Permissions;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
$menu = $event->menu;
$title = trim(trans_choice('general.companies', 1));
if ($this->canAccessMenuItem($title, 'read-settings-company')) {
$menu->route('settings.company.edit', $title, [], 10, ['icon' => 'business', 'search_keywords' => trans('settings.company.search_keywords')]);
}
$title = trim(trans_choice('general.localisations', 1));
if ($this->canAccessMenuItem($title, 'read-settings-localisation')) {
$menu->route('settings.localisation.edit', $title, [], 20, ['icon' => 'flag', 'search_keywords' => trans('settings.localisation.search_keywords')]);
}
$title = trim(trans_choice('general.invoices', 1));
if ($this->canAccessMenuItem($title, 'read-settings-invoice')) {
$menu->route('settings.invoice.edit', $title, [], 30, ['icon' => 'description', 'search_keywords' => trans('settings.invoice.search_keywords')]);
}
$title = trim(trans_choice('general.defaults', 1));
if ($this->canAccessMenuItem($title, 'read-settings-defaults')) {
$menu->route('settings.default.edit', $title, [], 40, ['icon' => 'tune', 'search_keywords' => trans('settings.default.search_keywords')]);
}
$title = trim(trans_choice('general.email_services', 1));
if ($this->canAccessMenuItem($title, 'read-settings-email')) {
$menu->route('settings.email.edit', $title, [], 50, ['icon' => 'email', 'search_keywords' => trans('settings.email_services.search_keywords')]);
}
$title = trim(trans_choice('general.email_templates', 2));
if ($this->canAccessMenuItem($title, 'read-settings-email-templates')) {
$menu->route('settings.email-templates.edit', $title, [], 60, ['icon' => 'attach_email', 'search_keywords' => trans('settings.email.templates.search_keywords')]);
}
$title = trim(trans('settings.scheduling.name'));
if ($this->canAccessMenuItem($title, 'read-settings-schedule')) {
$menu->route('settings.schedule.edit', $title, [], 70, ['icon' => 'alarm', 'search_keywords' => trans('settings.scheduling.search_keywords')]);
}
$title = trim(trans_choice('general.categories', 2));
if ($this->canAccessMenuItem($title, 'read-settings-categories')) {
$menu->route('categories.index', $title, [], 80, ['icon' => 'folder', 'search_keywords' => trans('settings.categories.search_keywords')]);
}
$title = trim(trans_choice('general.currencies', 2));
if ($this->canAccessMenuItem($title, 'read-settings-currencies')) {
$menu->route('currencies.index', $title, [], 90, ['icon' => 'attach_money', 'search_keywords' => trans('settings.currencies.search_keywords')]);
}
$title = trim(trans_choice('general.taxes', 2));
if ($this->canAccessMenuItem($title, 'read-settings-taxes')) {
$menu->route('taxes.index', $title, [], 100, ['icon' => 'percent', 'search_keywords' => trans('settings.taxes.search_keywords')]);
}
}
}

View File

@@ -8,8 +8,8 @@ use App\Jobs\Common\DeleteDashboard;
use App\Jobs\Common\DeleteReport;
use App\Jobs\Setting\DeleteEmailTemplate;
use App\Models\Common\Dashboard;
use App\Models\Common\EmailTemplate;
use App\Models\Common\Report;
use App\Models\Setting\EmailTemplate;
use App\Traits\Jobs;
use Throwable;

View File

@@ -30,7 +30,7 @@ class AddAccounts extends Listener
// send true for add limit on search and filter..
$event->class->filters['accounts'] = $this->getAccounts(true);
$event->class->filters['routes']['accounts'] = 'accounts.index';
$event->class->filters['routes']['accounts'] = ['accounts.index', 'search=enabled:1'];
}
/**

View File

@@ -28,7 +28,7 @@ class AddCustomers extends Listener
}
$event->class->filters['customers'] = $this->getCustomers(true);
$event->class->filters['routes']['customers'] = 'customers.index';
$event->class->filters['routes']['customers'] = ['customers.index', 'search=enabled:1'];
}
/**

View File

@@ -70,5 +70,11 @@ class AddExpenseCategories extends Listener
}
$this->setRowNamesAndValues($event, $rows);
$event->class->row_tree_nodes = [];
$nodes = $this->getCategoriesNodes($rows);
$this->setTreeNodes($event, $nodes);
}
}

View File

@@ -70,5 +70,11 @@ class AddIncomeCategories extends Listener
}
$this->setRowNamesAndValues($event, $rows);
$event->class->row_tree_nodes = [];
$nodes = $this->getCategoriesNodes($rows);
$this->setTreeNodes($event, $nodes);
}
}

View File

@@ -66,48 +66,46 @@ class AddIncomeExpenseCategories extends Listener
return;
}
switch (get_class($event->class)) {
case 'App\Reports\ProfitLoss':
$categories = Category::type(['income', 'expense'])->orderBy('name')->get();
$rows = $categories->pluck('name', 'id')->toArray();
$categories = Category::type(['income', 'expense'])->orderBy('name')->get();
$rows = $categories->pluck('name', 'id')->toArray();
$this->setRowNamesAndValuesForProfitLoss($event, $rows, $categories);
$this->setRowNamesAndValuesForCategories($event, $rows, $categories);
break;
case 'App\Reports\IncomeExpenseSummary':
$all_categories = $this->getIncomeExpenseCategories();
$nodes = $this->getCategoriesNodes($rows);
if ($category_ids = $this->getSearchStringValue('category_id')) {
$categories = explode(',', $category_ids);
$rows = collect($all_categories)->filter(function ($value, $key) use ($categories) {
return in_array($key, $categories);
});
} else {
$rows = $all_categories;
}
$this->setRowNamesAndValues($event, $rows);
break;
}
$this->setTreeNodesForCategories($event, $nodes, $categories);
}
public function setRowNamesAndValuesForProfitLoss($event, $rows, $categories)
public function setRowNamesAndValuesForCategories($event, $rows, $categories)
{
foreach ($event->class->dates as $date) {
foreach ($event->class->tables as $type_id => $type_name) {
foreach ($event->class->tables as $table_key => $table_name) {
foreach ($rows as $id => $name) {
$category = $categories->where('id', $id)->first();
if ($category->type != $type_id) {
if ($category->type != $table_key) {
continue;
}
$event->class->row_names[$type_name][$id] = $name;
$event->class->row_values[$type_name][$id][$date] = 0;
$event->class->row_names[$table_key][$id] = $name;
$event->class->row_values[$table_key][$id][$date] = 0;
}
}
}
}
public function setTreeNodesForCategories($event, $nodes, $categories)
{
foreach ($event->class->tables as $table_key => $table_name) {
foreach ($nodes as $id => $node) {
$category = $categories->where('id', $id)->first();
if ($category->type != $table_key) {
continue;
}
$event->class->row_tree_nodes[$table_key][$id] = $node;
}
}
}
}

View File

@@ -42,7 +42,7 @@ class AddSearchString extends Listener
foreach ($old as $key => $value) {
$filter = $key . ':' . $value;
if (!in_array($filter, $filters)) {
if (! in_array($filter, $filters)) {
$filters[] = $filter;
}
}

View File

@@ -28,7 +28,7 @@ class AddVendors extends Listener
}
$event->class->filters['vendors'] = $this->getVendors(true);
$event->class->filters['routes']['vendors'] = 'vendors.index';
$event->class->filters['routes']['vendors'] = ['vendors.index', 'search=enabled:1'];
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2014 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.14';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2017 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.17';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2020 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.20';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2023 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.23';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,54 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Traits\Permissions;
use Illuminate\Support\Facades\Artisan;
class Version2024 extends Listener
{
use Permissions;
const ALIAS = 'core';
const VERSION = '2.0.24';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updateDatabase();
$this->updatePermissions();
}
public function updateDatabase()
{
Artisan::call('migrate', ['--force' => true]);
}
public function updatePermissions()
{
$this->attachPermissionsByRoleNames([
'admin' => [
'banking-transactions' => 'c,r,u,d',
'common-notifications' => 'c,r,u,d',
'common-uploads' => 'r,d',
],
'manager' => [
'banking-transactions' => 'c,r,u,d',
'common-notifications' => 'c,r,u,d',
],
]);
}
}

View File

@@ -1,52 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Common\Company;
use App\Utilities\Overrider;
class Version203 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.3';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updateCompanies();
}
protected function updateCompanies()
{
$company_id = company_id();
$companies = Company::cursor();
foreach ($companies as $company) {
$company->makeCurrent();
$this->updateSettings($company);
}
company($company_id)->makeCurrent();
}
public function updateSettings($company)
{
setting()->set(['invoice.payment_terms' => setting('invoice.payment_terms', 0)]);
setting()->save();
}
}

View File

@@ -1,100 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\DB;
class Version205 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.5';
protected $items;
protected $categories;
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->items = [];
$this->updateBillItems();
$this->updateInvoiceItems();
}
protected function updateBillItems()
{
$bill_items = DB::table('bill_items')->whereNull('deleted_at')->where('item_id', 0)->cursor();
foreach ($bill_items as $bill_item) {
$item_id = $this->getItemId($bill_item);
DB::table('bill_items')
->where('id', $bill_item->id)
->update(['item_id' => $item_id]);
}
}
protected function updateInvoiceItems()
{
$invoice_items = DB::table('invoice_items')->whereNull('deleted_at')->where('item_id', 0)->cursor();
foreach ($invoice_items as $invoice_item) {
$item_id = $this->getItemId($invoice_item);
DB::table('invoice_items')
->where('id', $invoice_item->id)
->update(['item_id' => $item_id]);
DB::table('items')
->where('id', $item_id)
->update(['sale_price' => $invoice_item->price]);
}
}
protected function getItemId($item)
{
// Set category_id for company.
if (!isset($this->categories[$item->company_id])) {
$this->categories[$item->company_id] = DB::table('categories')->where('company_id', $item->company_id)->where('type', 'item')->first()->id;
}
// Return set item_id for item name.
if (isset($this->items[$item->company_id]) && in_array($item->name, $this->items[$item->company_id])) {
return array_search($item->name, $this->items[$item->company_id]);
}
// Insert new item.
$item_id = DB::table('items')->insertGetId([
'company_id' => $item->company_id,
'name' => $item->name,
'description' => null,
'sale_price' => $item->price,
'purchase_price' => $item->price,
'category_id' => $this->categories[$item->company_id],
'tax_id' => null,
'enabled' => 1,
'created_at' => $item->created_at,
'updated_at' => $item->updated_at,
'deleted_at' => null,
]);
// Set item_id for item name.
$this->items[$item->company_id][$item_id] = $item->name;
return $item_id;
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Utilities\Installer;
class Version207 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.7';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
// Update .env file
Installer::updateEnv([
'MAIL_MAILER' => env('MAIL_DRIVER', config('mail.default')),
]);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version208 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.8';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V20;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version209 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.0.9';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('view:clear');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Common\Company;
use App\Models\Common\Media;
use App\Utilities\Date;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
class Version2112 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.12';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updateDatabase();
$this->updateCompanies();
}
public function updateDatabase()
{
DB::table('migrations')->insert([
'id' => DB::table('migrations')->max('id') + 1,
'migration' => '2016_06_27_000001_create_mediable_test_tables',
'batch' => DB::table('migrations')->max('batch') + 1,
]);
Artisan::call('migrate', ['--force' => true]);
}
public function updateCompanies()
{
$companies = Company::withTrashed()->cursor();
foreach ($companies as $company) {
$this->moveMedia($company);
}
}
public function moveMedia($company)
{
$medias = Media::inDirectory('uploads', $company->id . '/', true)->cursor();
foreach ($medias as $media) {
// Bizarre record
if (empty($media->directory) || empty($media->basename)) {
$media->delete();
continue;
}
// Delete media from db if file not exists
if (!Storage::exists($media->directory . '/' . $media->basename)) {
$media->delete();
continue;
}
// Delete completely if soft deleted
if (!empty($media->deleted_at)) {
$media->delete();
Storage::delete($media->directory . '/' . $media->basename);
continue;
}
$date = Date::parse($media->created_at)->format('Y/m/d');
$new_folder = $date . '/'. $media->directory;
// Check if already exists and delete
if (Storage::exists($new_folder . '/' . $media->basename)) {
Storage::delete($new_folder . '/' . $media->basename);
}
$media->move($new_folder);
}
// Delete old company folder
File::deleteDirectory(Storage::path($company->id));
}
}

View File

@@ -1,83 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Common\Media;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use App\Scopes\Company;
use Illuminate\Support\Facades\Schema;
class Version2114 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.14';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$migration = DB::table('migrations')
->where('migration', '2016_06_27_000001_create_mediable_test_tables')
->first();
if ($migration === null) {
DB::table('migrations')->insert([
'id' => DB::table('migrations')->max('id') + 1,
'migration' => '2016_06_27_000001_create_mediable_test_tables',
'batch' => DB::table('migrations')->max('batch') + 1,
]);
}
Artisan::call('migrate', ['--force' => true]);
$this->updateMediaTables();
}
public function updateMediaTables()
{
$company_ids = [];
foreach (Media::withTrashed()->withoutGlobalScope(Company::class)->cursor() as $media) {
$company_id = null;
if (preg_match('/\d{4}(\/\d{2}){2}\/(\d+)\//', $media->directory, $matches) && isset($matches[2])) { // 2021/04/09/34235/invoices
$company_id = $matches[2];
} elseif (preg_match('/^(\d+)\//', $media->directory, $matches) && isset($matches[1])) { // 34235/invoices
$company_id = $matches[1];
}
if (null === $company_id) {
continue;
}
$company_ids[$company_id][] = $media->id;
}
foreach ($company_ids as $company_id => $media_ids) {
DB::table('media')->whereIn('id', $media_ids)->update(['company_id' => $company_id]);
DB::table('mediables')->whereIn('media_id', $media_ids)->update(['company_id' => $company_id]);
}
Schema::table('media', function (Blueprint $table) {
$table->unsignedInteger('company_id')->default(null)->change();
});
Schema::table('mediables', function (Blueprint $table) {
$table->unsignedInteger('company_id')->default(null)->change();
});
}
}

View File

@@ -1,43 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
class Version2116 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.16';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$migration = DB::table('migrations')
->where('migration', '2016_06_27_000001_create_mediable_test_tables')
->first();
if ($migration === null) {
DB::table('migrations')->insert([
'id' => DB::table('migrations')->max('id') + 1,
'migration' => '2016_06_27_000001_create_mediable_test_tables',
'batch' => DB::table('migrations')->max('batch') + 1,
]);
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,62 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Common\Company;
use App\Models\Common\Report;
use App\Utilities\Reports as Utility;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
class Version2117 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.17';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updateCompanies();
Artisan::call('migrate', ['--force' => true]);
}
protected function updateCompanies()
{
$company_id = company_id();
$companies = Company::cursor();
foreach ($companies as $company) {
$company->makeCurrent();
$this->cacheReports();
}
company($company_id)->makeCurrent();
}
protected function cacheReports()
{
try {
Report::all()->each(function ($report) {
Cache::put('reports.totals.' . $report->id, Utility::getClassInstance($report)->getGrandTotal());
});
} catch (\Throwable $e) {
report($e);
}
}
}

View File

@@ -1,58 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Common\Company;
use App\Models\Common\EmailTemplate;
use Illuminate\Support\Facades\Artisan;
class Version2118 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.18';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updateEmailTemplate();
Artisan::call('cache:clear');
Artisan::call('migrate', ['--force' => true]);
}
protected function updateEmailTemplate()
{
$company_id = company_id();
$companies = Company::cursor();
foreach ($companies as $company) {
$company->makeCurrent();
EmailTemplate::create([
'company_id' => $company->id,
'alias' => 'revenue_new_customer',
'class' => 'App\Notifications\Sale\Revenue',
'name' => 'settings.email.templates.revenue_new_customer',
'subject' => trans('email_templates.revenue_new_customer.subject'),
'body' => trans('email_templates.revenue_new_customer.body'),
]);
}
company($company_id)->makeCurrent();
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2124 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.24';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2125 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.25';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,37 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2126 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.26';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
$country_code = array_search(setting('company.country'), trans('countries'));
if ($country_code) {
setting()->set('company.country', $country_code);
setting()->save();
}
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2127 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.27';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Models\Common\Company;
use App\Utilities\Overrider;
class Version213 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.3';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updateCompanies();
}
protected function updateCompanies()
{
$company_id = company_id();
$companies = Company::cursor();
foreach ($companies as $company) {
$company->makeCurrent();
$this->updateSettings();
}
company($company_id)->makeCurrent();
}
public function updateSettings()
{
$company_logo = setting('company.logo');
if (is_array($company_logo)) {
setting()->set('company.logo', $company_logo['id']);
}
setting()->save();
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2133 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.33';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version2134 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.34';
/**
* Handle the event.
*
* @param $event
*
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -1,44 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Traits\Permissions;
class Version218 extends Listener
{
use Permissions;
const ALIAS = 'core';
const VERSION = '2.1.8';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
$this->updatePermissions();
}
public function updatePermissions()
{
// c=create, r=read, u=update, d=delete
$this->attachPermissionsByRoleNames([
'admin' => [
'widgets-currencies' => 'r',
],
'manager' => [
'widgets-currencies' => 'r',
],
]);
}
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Listeners\Update\V21;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use Illuminate\Support\Facades\Artisan;
class Version219 extends Listener
{
const ALIAS = 'core';
const VERSION = '2.1.9';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Artisan::call('migrate', ['--force' => true]);
}
}

View File

@@ -0,0 +1,656 @@
<?php
namespace App\Listeners\Update\V30;
use App\Abstracts\Listeners\Update as Listener;
use App\Events\Install\UpdateFinished as Event;
use App\Jobs\Common\CreateWidget;
use App\Jobs\Setting\CreateEmailTemplate;
use App\Jobs\Setting\UpdateEmailTemplate;
use App\Models\Auth\User;
use App\Models\Common\Company;
use App\Models\Common\Dashboard;
use App\Models\Document\Document;
use App\Models\Setting\EmailTemplate;
use App\Models\Banking\Transaction;
use App\Models\Common\Recurring;
use App\Traits\Jobs;
use App\Traits\Permissions;
use App\Traits\Transactions;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Throwable;
class Version300 extends Listener
{
use Jobs, Permissions, Transactions;
const ALIAS = 'core';
const VERSION = '3.0.0';
/**
* Handle the event.
*
* @param $event
* @return void
*/
public function handle(Event $event)
{
if ($this->skipThisUpdate($event)) {
return;
}
Log::channel('stderr')->info('Starting the Akaunting 3.0 update...');
$this->updateDatabase();
$this->deleteOldWidgets();
$this->updateCompanies();
$this->updatePermissions();
$this->deleteOldFiles();
$this->clearNotifications();
Log::channel('stderr')->info('Akaunting 3.0 update finished.');
}
public function updateDatabase()
{
Log::channel('stderr')->info('Updating database...');
DB::table('migrations')->insert([
'id' => DB::table('migrations')->max('id') + 1,
'migration' => '2019_11_16_000000_core_v2',
'batch' => DB::table('migrations')->max('batch') + 1,
]);
Artisan::call('migrate', ['--force' => true]);
Log::channel('stderr')->info('Database updated.');
}
public function updateCompanies()
{
Log::channel('stderr')->info('Updating companies...');
$company_id = company_id();
$companies = Company::cursor();
foreach ($companies as $company) {
Log::channel('stderr')->info('Updating company:' . $company->id);
$company->makeCurrent();
$this->createNewWidgets();
$this->updateEmailTemplates();
$this->updateRecurables();
$this->updateTransactions();
Log::channel('stderr')->info('Company updated:' . $company->id);
}
company($company_id)->makeCurrent();
Log::channel('stderr')->info('Companies updated.');
}
public function deleteOldWidgets()
{
Log::channel('stderr')->info('Deleting old widgets...');
// Delete old widgets
$old_widgets = [
'App\\Widgets\\TotalIncome',
'App\\Widgets\\TotalExpenses',
'App\\Widgets\\TotalProfit',
'App\\Widgets\\CashFlow',
'App\\Widgets\\IncomeByCategory',
'App\\Widgets\\ExpensesByCategory',
'App\\Widgets\\AccountBalance',
'App\\Widgets\\LatestIncome',
'App\\Widgets\\LatestExpenses',
];
DB::transaction(function () use ($old_widgets) {
DB::table('widgets')->whereIn('class', $old_widgets)->delete();
});
Log::channel('stderr')->info('Old widgets deleted.');
}
public function createNewWidgets()
{
Log::channel('stderr')->info('Creating new widgets...');
// Create new widgets
$new_widgets = [
'App\Widgets\Receivables',
'App\Widgets\Payables',
'App\Widgets\CashFlow',
'App\Widgets\ProfitLoss',
'App\Widgets\ExpensesByCategory',
'App\Widgets\AccountBalance',
'App\Widgets\BankFeeds',
];
Log::channel('stderr')->info('Creating new widgets...');
Dashboard::whereDoesntHave('widgets')->each(function($dashboard) use ($new_widgets) {
$sort = 1;
foreach ($new_widgets as $class_name) {
$class = new $class_name();
$this->dispatch(new CreateWidget([
'company_id' => $dashboard->company_id,
'dashboard_id' => $dashboard->id,
'class' => $class_name,
'name' => $class->getDefaultName(),
'sort' => $sort,
'settings' => $class->getDefaultSettings(),
]));
$sort++;
}
});
Log::channel('stderr')->info('New widgets created.');
}
public function updateEmailTemplates()
{
Log::channel('stderr')->info('Updating/Creating email templates...');
$payment_received_model = EmailTemplate::alias('revenue_new_customer')->first();
$payment_received_request = [
'company_id' => company_id(),
'alias' => 'payment_received_customer',
'class' => 'App\Notifications\Banking\Transaction',
'name' => 'settings.email.templates.payment_received_customer',
];
Log::channel('stderr')->info('Updating old email templates...');
if (!empty($payment_received_model)) {
$this->dispatch(new UpdateEmailTemplate($payment_received_model, array_merge($payment_received_request, [
'subject' => $payment_received_model->subject,
'body' => $payment_received_model->body,
])));
} else {
$this->dispatch(new CreateEmailTemplate(array_merge($payment_received_request, [
'subject' => trans('email_templates.payment_received_customer.subject'),
'body' => trans('email_templates.payment_received_customer.body'),
'created_from' => 'core::seed',
])));
}
Log::channel('stderr')->info('Creating new email templates...');
$this->dispatch(new CreateEmailTemplate([
'company_id' => company_id(),
'alias' => 'invoice_view_admin',
'class' => 'App\Notifications\Sale\Invoice',
'name' => 'settings.email.templates.invoice_view_admin',
'subject' => trans('email_templates.invoice_view_admin.subject'),
'body' => trans('email_templates.invoice_view_admin.body'),
'created_from' => 'core::seed',
]));
$this->dispatch(new CreateEmailTemplate([
'company_id' => company_id(),
'alias' => 'payment_made_vendor',
'class' => 'App\Notifications\Banking\Transaction',
'name' => 'settings.email.templates.payment_made_vendor',
'subject' => trans('email_templates.payment_made_vendor.subject'),
'body' => trans('email_templates.payment_made_vendor.body'),
'created_from' => 'core::seed',
]));
Log::channel('stderr')->info('Email templates updated/created.');
}
public function updateRecurables()
{
Log::channel('stderr')->info('Updating recurring...');
$recurrings = Recurring::with('recurable')->cursor();
foreach ($recurrings as $recurring) {
// Document or Transaction
$model = $recurring->recurable;
if ($model instanceof Document) {
$cloneable_relations = ['items', 'totals'];
$number_field = 'document_number';
} else {
$cloneable_relations = [];
$number_field = 'number';
}
$model->cloneable_relations = $cloneable_relations;
// Create the recurring template
$clone = $model->duplicate();
$clone->type = $clone->type . '-recurring';
$clone->$number_field = $this->getNextTransactionNumber('-recurring');
$clone->saveQuietly();
$this->increaseNextTransactionNumber('-recurring');
// Update the recurring table
$recurring->recurable_id = $clone->id;
$recurring->saveQuietly();
// Set the new recurring template as parent for the original model
$model->parent_id = $clone->id;
$model->saveQuietly();
// Set the new recurring template as parent for child models
DB::table($model->getTable())->where('parent_id', $model->id)->update([
'parent_id' => $clone->id,
'created_from' => 'core::recurring',
]);
}
Log::channel('stderr')->info('Recurring updated.');
}
public function updateTransactions()
{
Log::channel('stderr')->info('Updating transactions...');
$transactions = Transaction::isNotRecurring()->cursor();
$number = 1;
$transaction_number = $this->getTransactionNumber($number);
foreach ($transactions as $transaction) {
$transaction->number = $transaction_number;
$transaction->saveQuietly();
$number++;
$transaction_number = $this->getTransactionNumber($number);
}
$this->saveNextTransactionNumber($number);
Log::channel('stderr')->info('Transactions updated.');
}
public function clearNotifications()
{
try {
$users = User::all();
foreach ($users as $user) {
$notifications = $user->unreadNotifications;
foreach ($notifications as $notification) {
$notification->markAsRead();
}
}
} catch (\Exception $e) {}
}
public function getTransactionNumber($number): string
{
$prefix = setting('transaction.number_prefix');
$digit = setting('transaction.number_digit');
return $prefix . str_pad($number, $digit, '0', STR_PAD_LEFT);
}
public function saveNextTransactionNumber($next): void
{
setting(['transaction.number_next' => $next]);
setting()->save();
}
public function updatePermissions()
{
Log::channel('stderr')->info('Updating permissions...');
$rows = [
'accountant' => [
'admin-panel' => 'r',
'api' => 'r',
'common-dashboards' => 'r',
'common-items' => 'r',
'purchases-bills' => 'r',
'purchases-vendors' => 'r',
'sales-customers' => 'r',
'sales-invoices' => 'r',
'banking-accounts' => 'r',
'banking-reconciliations' => 'r',
'banking-transactions' => 'r',
'banking-transfers' => 'r',
'reports-expense-summary' => 'r',
'reports-income-summary' => 'r',
'reports-income-expense-summary' => 'r',
'reports-profit-loss' => 'r',
'reports-tax-summary' => 'r',
'modules-home' => 'r',
'modules-item' => 'r',
'modules-my' => 'r',
'modules-tiles' => 'r',
],
];
Log::channel('stderr')->info('Attaching new permissions...');
// c=create, r=read, u=update, d=delete
$this->attachPermissionsByRoleNames($rows);
// c=create, r=read, u=update, d=delete
$this->attachPermissionsToAdminRoles([
'settings-email-templates' => 'r,u',
'settings-company' => 'u',
'settings-defaults' => 'u',
'settings-email' => 'u',
'settings-invoice' => 'u',
'settings-localisation' => 'u',
'settings-schedule' => 'u',
'widgets-bank-feeds' => 'r',
'widgets-payables' => 'r',
'widgets-profit-loss' => 'r',
'widgets-receivables' => 'r',
]);
Log::channel('stderr')->info('Dettaching old permissions...');
// c=create, r=read, u=update, d=delete
$this->detachPermissionsFromAdminRoles([
'auth-permissions' => 'c,r,u,d',
'auth-roles' => 'c,r,u,d',
'common-notifications' => 'c,r,u,d',
'purchases-payments' => 'c,r,u,d',
'sales-revenues' => 'c,r,u,d',
'settings-settings' => 'r,u',
'settings-settings' => 'r,u',
'widgets-income-by-category' => 'r',
'widgets-latest-expenses' => 'r',
'widgets-latest-income' => 'r',
'widgets-total-expenses' => 'r',
'widgets-total-income' => 'r',
'widgets-total-profit' => 'r',
]);
Log::channel('stderr')->info('Permissions updated.');
}
public function deleteOldFiles()
{
Log::channel('stderr')->info('Deleting old files and folders...');
$files = [
'app/Abstracts/View/Components/Document.php',
'app/Abstracts/View/Components/DocumentForm.php',
'app/Abstracts/View/Components/DocumentIndex.php',
'app/Abstracts/View/Components/DocumentShow.php',
'app/Abstracts/View/Components/DocumentTemplate.php',
'app/Abstracts/View/Components/Transaction.php',
'app/Abstracts/View/Components/TransactionShow.php',
'app/Abstracts/View/Components/TransactionTemplate.php',
'app/Http/BulkActions/Auth/Permissions.php',
'app/Http/BulkActions/Auth/Roles.php',
'app/Http/BulkActions/Purchases/Payments.php',
'app/Http/BulkActions/Sales/Revenues.php',
'app/Http/Exports/Purchases/Payments.php',
'app/Http/Exports/Sales/Revenues.php',
'app/Http/Imports/Purchases/Payments.php',
'app/Http/Imports/Sales/Revenues.php',
'app/Http/Controllers/Api/Auth/Permissions.php',
'app/Http/Controllers/Api/Auth/Roles.php',
'app/Http/Controllers/Auth/Permissions.php',
'app/Http/Controllers/Auth/Roles.php',
'app/Http/Controllers/Common/Notifications.php',
'app/Http/Controllers/Purchases/Payments.php',
'app/Http/Controllers/Sales/Revenues.php',
'app/Http/Controllers/Settings/Settings.php',
'app/Http/Requests/Auth/Permission.php',
'app/Http/Requests/Auth/Role.php',
'app/Http/ViewComposers/Header.php',
'app/Http/ViewComposers/Index.php',
'app/Http/ViewComposers/Notifications.php',
'app/Http/ViewComposers/Show.php',
'app/Http/ViewComposers/Suggestions.php',
'app/Http/ViewComposers/Menu.php',
'app/Http/ViewComposers/Logo.php',
'app/Http/ViewComposers/Modules.php',
'app/Http/ViewComposers/Wizard.php',
'app/Models/Common/EmailTemplate.php',
'app/Notifications/Sale/Revenue.php',
'app/Transformers/Auth/Permission.php',
'app/Transformers/Auth/Role.php',
'app/View/Components/Documents/Index/CardBody.php',
'app/View/Components/Documents/Index/CardFooter.php',
'app/View/Components/Documents/Index/CardHeader.php',
'app/View/Components/Documents/Index/TopButtons.php',
'app/View/Components/Documents/Show/Timeline.php',
'app/View/Components/Documents/Show/Transactions.php',
'app/View/Components/Transactions/Show/Header.php',
'app/View/Components/Transactions/Show/Footer.php',
'app/View/Components/Transfers/Show/Header.php',
'app/View/Components/Transfers/Show/Footer.php',
'app/Widgets/IncomeByCategory.php',
'app/Widgets/LatestExpenses.php',
'app/Widgets/LatestIncome.php',
'app/Widgets/TotalExpense.php',
'app/Widgets/TotalIncome.php',
'app/Widgets/TotalProfit.php',
'database/factories/Permission.php',
'database/factories/Role.php',
'database/migrations/2020_01_08_000000_core_v200.php',
'database/migrations/2020_03_20_183732_core_v208.php',
'database/migrations/2020_06_09_000000_core_v2014.php',
'database/migrations/2020_07_20_000000_core_v2017.php',
'database/migrations/2020_10_13_000000_core_v210.php',
'database/migrations/2021_04_01_000000_core_v219.php',
'database/migrations/2021_05_17_000000_core_v2114.php',
'database/migrations/2021_06_17_000000_core_v2117.php',
'database/migrations/2021_09_01_000000_core_v2124.php',
'database/migrations/2021_09_10_000000_core_v2125.php',
'database/migrations/2021_09_10_000000_core_v2126.php',
'database/migrations/2021_09_10_000000_core_v2127.php',
'database/migrations/2022_03_02_000000_core_v2133.php',
'database/migrations/2022_03_23_000000_core_v2134.php',
'public/files/import/payments.xlsx',
'public/files/import/revenues.xlsx',
'resources/assets/js/views/auth/permissions.js',
'resources/assets/js/views/auth/roles.js',
'resources/views/components/documents/index/card-body.blade.php',
'resources/views/components/documents/index/card-footer.blade.php',
'resources/views/components/documents/index/card-header.blade.php',
'resources/views/components/documents/index/top-buttons.blade.php',
'resources/views/components/documents/show/timeline.blade.php',
'resources/views/components/documents/show/transactions.blade.php',
'resources/views/components/form-group-title.blade.php',
'resources/views/components/transactions/show/header.blade.php',
'resources/views/components/transactions/show/footer.blade.php',
'resources/views/components/transfers/show/header.blade.php',
'resources/views/components/transfers/show/footer.blade.php',
'resources/views/components/layouts/modules.php',
'resources/views/layouts/admin.blade.php',
'resources/views/layouts/auth.blade.php',
'resources/views/layouts/install.blade.php',
'resources/views/layouts/maintenance.blade.php',
'resources/views/layouts/modules.blade.php',
'resources/views/layouts/portal.blade.php',
'resources/views/layouts/print.blade.php',
'resources/views/layouts/signed.blade.php',
'resources/views/layouts/wizard.blade.php',
'resources/views/partials/admin/content.blade.php',
'resources/views/partials/admin/empty_page.blade.php',
'resources/views/partials/admin/favorites.blade.php',
'resources/views/partials/admin/footer.blade.php',
'resources/views/partials/admin/head.blade.php',
'resources/views/partials/admin/header.blade.php',
'resources/views/partials/admin/menu.blade.php',
'resources/views/partials/admin/pagination.blade.php',
'resources/views/partials/admin/scripts.blade.php',
'resources/views/partials/admin/suggestions.blade.php',
'resources/views/partials/auth/head.blade.php',
'resources/views/partials/auth/scripts.blade.php',
'resources/views/partials/form/bulk_action_all_group.blade.php',
'resources/views/partials/form/bulk_action_group.blade.php',
'resources/views/partials/form/bulk_action_row_group.blade.php',
'resources/views/partials/form/checkbox_group.blade.php',
'resources/views/partials/form/date_group.blade.php',
'resources/views/partials/form/date_range.blade.php',
'resources/views/partials/form/date_time_group.blade.php',
'resources/views/partials/form/delete_button.blade.php',
'resources/views/partials/form/delete_link.blade.php',
'resources/views/partials/form/email_group.blade.php',
'resources/views/partials/form/enabled_group.blade.php',
'resources/views/partials/form/file_group.blade.php',
'resources/views/partials/form/invoice_text.blade.php',
'resources/views/partials/form/money_group.blade.php',
'resources/views/partials/form/multi_select_add_new_group.blade.php',
'resources/views/partials/form/multi_select_group.blade.php',
'resources/views/partials/form/multi_select_remote_add_new_group.blade.php',
'resources/views/partials/form/multi_select_remote_group.blade.php',
'resources/views/partials/form/number_group.blade.php',
'resources/views/partials/form/password_group.blade.php',
'resources/views/partials/form/radio_group.blade.php',
'resources/views/partials/form/recurring.blade.php',
'resources/views/partials/form/save_buttons.blade.php',
'resources/views/partials/form/select_add_new_group.blade.php',
'resources/views/partials/form/select_group.blade.php',
'resources/views/partials/form/select_group_add_new_group.blade.php',
'resources/views/partials/form/select_group_group.blade.php',
'resources/views/partials/form/select_remote_add_new_group.blade.php',
'resources/views/partials/form/select_remote_group.blade.php',
'resources/views/partials/form/text_editor_group.blade.php',
'resources/views/partials/form/text_group.blade.php',
'resources/views/partials/form/textarea_group.blade.php',
'resources/views/partials/form/time_group.blade.php',
'resources/views/partials/email/body.blade.php',
'resources/views/partials/email/footer.blade.php',
'resources/views/partials/media/file.blade.php',
'resources/views/partials/reports/detail.blade.php',
'resources/views/partials/reports/detail/content/footer.blade.php',
'resources/views/partials/reports/detail/content/header.blade.php',
'resources/views/partials/reports/detail/table.blade.php',
'resources/views/partials/reports/detail/table/body.blade.php',
'resources/views/partials/reports/detail/table/footer.blade.php',
'resources/views/partials/reports/detail/table/header.blade.php',
'resources/views/partials/reports/detail/table/row.blade.php',
'resources/views/partials/reports/fields.blade.php',
'resources/views/partials/reports/filter.blade.php',
'resources/views/partials/reports/header.blade.php',
'resources/views/partials/reports/print.blade.php',
'resources/views/partials/reports/show.blade.php',
'resources/views/partials/reports/summary.blade.php',
'resources/views/partials/reports/summary/chart.blade.php',
'resources/views/partials/reports/summary/content/footer.blade.php',
'resources/views/partials/reports/summary/content/header.blade.php',
'resources/views/partials/reports/summary/table.blade.php',
'resources/views/partials/reports/summary/table/body.blade.php',
'resources/views/partials/reports/summary/table/footer.blade.php',
'resources/views/partials/reports/summary/table/header.blade.php',
'resources/views/partials/reports/summary/table/row.blade.php',
'resources/views/partials/install/head.blade.php',
'resources/views/partials/install/scripts.blade.php',
'resources/views/partials/maintenance/body.blade.php',
'resources/views/partials/maintenance/head.blade.php',
'resources/views/partials/modules/bar.blade.php',
'resources/views/partials/modules/head.blade.php',
'resources/views/partials/modules/item.blade.php',
'resources/views/partials/modules/items.blade.php',
'resources/views/partials/modules/my_apps_item.blade.php',
'resources/views/partials/modules/no_apps.blade.php',
'resources/views/partials/modules/pre_sale.blade.php',
'resources/views/partials/modules/releases.blade.php',
'resources/views/partials/modules/reviews.blade.php',
'resources/views/partials/modules/show/price.blade.php',
'resources/views/partials/modules/show/toggle.blade.php',
'resources/views/partials/portal/content.blade.php',
'resources/views/partials/portal/footer.blade.php',
'resources/views/partials/portal/head.blade.php',
'resources/views/partials/portal/header.blade.php',
'resources/views/partials/portal/menu.blade.php',
'resources/views/partials/portal/navbar.blade.php',
'resources/views/partials/portal/pagination.blade.php',
'resources/views/partials/portal/payment_method/hosted.blade.php',
'resources/views/partials/portal/payment_method/redirect.blade.php',
'resources/views/partials/portal/scripts.blade.php',
'resources/views/partials/print/head.blade.php',
'resources/views/partials/print/scripts.blade.php',
'resources/views/partials/pwa/pwa.blade.php',
'resources/views/partials/signed/content.blade.php',
'resources/views/partials/signed/footer.blade.php',
'resources/views/partials/signed/head.blade.php',
'resources/views/partials/wizard/head.blade.php',
'resources/views/partials/wizard/scripts.blade.php',
'resources/views/partials/wizard/steps.blade.php',
'resources/views/partials/widgets/header.blade.php',
'resources/views/settings/settings/index.blade.php',
'resources/views/widgets/latest_expenses.blade.php',
'resources/views/widgets/latest_income.blade.php',
'resources/views/widgets/total_expenses.blade.php',
'resources/views/widgets/total_income.blade.php',
'resources/views/widgets/total_profit.blade.php',
];
$directories = [
'app/Listeners/Update/V20',
'app/Listeners/Update/V21',
'modules/BC21',
'modules/Bc21',
'resources/views/auth/permissions',
'resources/views/auth/roles',
'resources/views/common/notifications',
'resources/views/purchases/payments',
'resources/views/sales/revenues',
'resources/views/layouts',
'resources/views/partials/admin',
'resources/views/partials/auth',
'resources/views/partials/form',
'resources/views/partials/install',
'resources/views/partials/maintenance',
'resources/views/partials/modules/show',
'resources/views/partials/modules',
'resources/views/partials/portal/payment_method',
'resources/views/partials/portal',
'resources/views/partials/print',
'resources/views/partials/pwa',
'resources/views/partials/signed',
'resources/views/partials/wizard',
'resources/views/partials/email',
'resources/views/partials/media',
'resources/views/partials/reports/details/content',
'resources/views/partials/reports/details/table',
'resources/views/partials/reports/details',
'resources/views/partials/reports/summary/content',
'resources/views/partials/reports/summary/table',
'resources/views/partials/reports/summary',
'resources/views/partials/reports',
'resources/views/partials/widgets',
];
Log::channel('stderr')->info('Deleting old files...');
foreach ($files as $file) {
File::delete(base_path($file));
}
Log::channel('stderr')->info('Deleting old folders...');
foreach ($directories as $directory) {
File::deleteDirectory(base_path($directory));
}
Log::channel('stderr')->info('Old files and folders deleted.');
}
}