Commit Graph

419 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
4f47db9258 Move Mobile/AuthController to Company/Auth and remove Mobile namespace
The Mobile namespace only contained an API auth controller (Sanctum token
login/logout/check) that is not mobile-specific. Relocated to
Company/Auth/AuthController alongside the other auth controllers.
2026-04-03 19:19:09 +02:00
Darko Gjorgjijoski
64c481e963 Rename controller namespaces: drop V1 prefix, clarify roles
V1/Admin     -> Company       (company-scoped controllers)
V1/SuperAdmin -> Admin        (platform-wide admin controllers)
V1/Customer  -> CustomerPortal (customer-facing portal)
V1/Installation -> Setup      (installation wizard)
V1/PDF       -> Pdf           (consistent casing)
V1/Modules   -> Modules       (drop V1 prefix)
V1/Webhook   -> Webhook       (drop V1 prefix)

The V1 prefix served no purpose - API versioning is in the route prefix
(/api/v1/), not the controller namespace. "Admin" was misleading for
company-scoped controllers. "SuperAdmin" is now simply "Admin" for
platform administration.
2026-04-03 19:15:20 +02:00
Darko Gjorgjijoski
0aaf0419c3 Reorganize Admin/General: 14 controllers down to 6
Move global reference data to SuperAdmin:
- CountriesController, CurrenciesController (not company-scoped)

Merge exchange rate operations into ExchangeRateProviderController:
- GetAllUsedCurrenciesController -> usedCurrenciesWithoutRate()
- BulkExchangeRateController -> bulkUpdate()

Consolidate single-action controllers:
- DateFormatsController + TimeFormatsController + TimezonesController -> FormatsController
- NextNumberController + NumberPlaceholdersController -> SerialNumberController
- SearchUsersController merged into SearchController::users()
2026-04-03 19:03:57 +02:00
Darko Gjorgjijoski
c0454613a8 Move customer stats logic from CustomerStatsController to CustomerService 2026-04-03 18:10:59 +02:00
Darko Gjorgjijoski
92872e7e1c Merge ShowReceiptController and UploadReceiptController into ExpensesController 2026-04-03 18:07:07 +02:00
Darko Gjorgjijoski
2191417151 Consolidate ExchangeRate single-action controllers into ExchangeRateProviderController
Merge 4 invocable controllers (GetActiveProvider, GetExchangeRate,
GetSupportedCurrencies, GetUsedCurrencies) as methods on the existing
resource controller: activeProvider(), getRate(), supportedCurrencies(),
usedCurrencies().
2026-04-03 18:03:24 +02:00
Darko Gjorgjijoski
5f389ea3b0 Consolidate single-action controllers into resource controllers
Merge 11 single-action controllers into their parent resource controllers:
- Invoice: send, sendPreview, clone, changeStatus -> InvoicesController
- Estimate: send, sendPreview, clone, convertToInvoice, changeStatus -> EstimatesController
- Payment: send, sendPreview -> PaymentsController

Extract clone and convert business logic from controllers into services:
- InvoiceService: add clone(), changeStatus()
- EstimateService: add clone(), convertToInvoice(), changeStatus()

Previously this logic was inlined in controllers (~80-90 lines each).
2026-04-03 17:55:46 +02:00
Darko Gjorgjijoski
f76f351244 Merge CompanyController into CompaniesController as show() method 2026-04-03 17:45:20 +02:00
Darko Gjorgjijoski
1ca915a0a3 Split CompanyController and introduce standalone User Settings page
Backend:
- Extract user profile methods (show, update, uploadAvatar) from
  CompanyController into new UserProfileController
- CompanyController now only handles company concerns (updateCompany,
  uploadCompanyLogo)
- Remove Account Settings from setting_menu config

Frontend:
- New /admin/user-settings page with 3 tabs: General, Profile Photo,
  Security (password change)
- User dropdown now links to /admin/user-settings instead of
  /admin/settings/account-settings
- Settings sidebar defaults to Company Information as first item
- Remove old monolithic AccountSetting.vue
2026-04-03 17:35:41 +02:00
Darko Gjorgjijoski
6b5e4878fb Consolidate Admin Settings controllers: merge Get/Update pairs
Merge GetCompanySettingsController + UpdateCompanySettingsController into
CompanySettingsController with show() and update() methods.

Merge GetUserSettingsController + UpdateUserSettingsController into
UserSettingsController with show() and update() methods.

Absorb GetCompanyMailConfigurationController into
CompanyMailConfigurationController as getDefaultConfig() method.

Removes 5 single-action controllers, down to 4 Settings controllers.
2026-04-03 17:18:48 +02:00
Darko Gjorgjijoski
bbf46577dc Move global admin controllers from Admin to SuperAdmin namespace
Backup, Update, Modules, and global Settings controllers (mail config,
PDF config, disk management, global settings) are application-wide features
not scoped to a company. Move them from Admin/ to SuperAdmin/ to match the
v3.0 UI structure where these live under Administration.

Company-scoped settings controllers remain in Admin/Settings/.
2026-04-03 16:52:18 +02:00
Darko Gjorgjijoski
de06c335ac Remove dead code from app/Http: unused middleware, requests, and resources
Delete 23 unused files:
- AdminMiddleware (never registered, SuperAdminMiddleware used instead)
- 4 form requests with no controller references
- AbilityResource/Collection and ExchangeRateLogResource/Collection (zero usage)
- Customer/RecurringInvoiceResource and Collection (no controller or nested reference)
- 10 Customer Collection classes whose Resources are only used via new, never ::collection()
2026-04-03 16:41:33 +02:00
Darko Gjorgjijoski
62f31960ab Move CustomPathGenerator from app/Generators to app/Support 2026-04-03 16:21:36 +02:00
Darko Gjorgjijoski
129028518d Consolidate PDF classes under app/Services/Pdf with consistent naming
Split PDFService.php (3 classes + 2 interfaces in one file) into separate
files. Move GotenbergPDFDriver from app/Services/PDFDrivers/ into
app/Services/Pdf/. Normalize casing from ALL-CAPS PDF to Pdf throughout:
facade, provider, service, driver factory, and Gotenberg driver.

Fix PaymentService using Barryvdh DomPDF facade directly instead of the
app's PDF facade (bypassed the driver factory). Report controllers also
updated to use the app facade.
2026-04-03 16:18:25 +02:00
Darko Gjorgjijoski
e0b8b86e06 Rename SerialNumberFormatter to SerialNumberService for consistency 2026-04-03 16:09:22 +02:00
Darko Gjorgjijoski
e208e3ba56 Move Hashids classes from app/Hashids to app/Services/Hashids 2026-04-03 15:41:05 +02:00
Darko Gjorgjijoski
0ce88ab817 Remove app/Space folder and extract model business logic into services
Relocate all 14 files from the catch-all app/Space namespace into proper
locations: data providers to Support/Formatters, installation utilities to
Services/Installation, PDF utils to Services/Pdf, module/update classes to
Services/Module and Services/Update, SiteApi trait to Traits, and helpers
to Support.

Extract ~1,400 lines of business logic from 8 fat models (Invoice, Payment,
Estimate, RecurringInvoice, Company, Customer, Expense, User) into 9 new
service classes with constructor injection. Controllers now depend on
services instead of calling static model methods. Shared item/tax creation
logic consolidated into DocumentItemService.
2026-04-03 15:37:22 +02:00
Darko Gjorgjijoski
23ff69026e Merge branch 'master' into v3.0 2026-04-03 14:36:24 +02:00
Darko Gjorgjijoski
7d9fdb79cc Scope users listing and search to current company (#607)
Add scopeWhereCompany() to User model using whereHas through the
user_company pivot table. Apply it in UsersController::index() and
SearchController so users only see members of their current company.

Previously, the users page showed ALL users across all companies.

Ref #574
2026-04-03 14:34:33 +02:00
Darko Gjorgjijoski
3d871604ae Add company ownership check to clone endpoints (#606)
Verify the source record belongs to the current company before cloning.
Previously, users could clone invoices/estimates from other companies,
leaking sensitive data (amounts, customer details, items, taxes, notes).

The view policy already includes hasCompany() check, so authorizing
view on the source record gates both ability and company ownership.

Ref #574
2026-04-03 14:32:12 +02:00
Darko Gjorgjijoski
1adebe85b9 Scope all bulk deletes to current company and fix inverted ownership transfer (#605)
Bulk delete: filter IDs through whereCompany() before deleting in all
controllers (Invoices, Payments, Items, Expenses, Estimates, Recurring
Invoices). Previously, any user could delete records from other companies
by providing cross-company IDs.

Transfer ownership: fix inverted hasCompany() check that allowed
transferring company ownership to users who do NOT belong to the company,
while blocking users who DO belong.

Ref #567
2026-04-03 14:16:42 +02:00
Darko Gjorgjijoski
defbfc6406 Fix CustomerPolicy missing hasCompany() check (IDOR) (#604)
* Fix CustomerPolicy missing hasCompany() check (cross-company IDOR)

Add $user->hasCompany($customer->company_id) check to view, update,
delete, restore, and forceDelete methods in CustomerPolicy, matching
the pattern used by all other policies (InvoicePolicy, PaymentPolicy,
EstimatePolicy, etc.).

Without this check, a user in Company A with view-customer ability
could access customers belonging to Company B by providing the target
customer's ID.

Add cross-company authorization tests to verify the fix.

Closes #565

* Scope bulk delete to current company to prevent cross-company deletion

Filter customer IDs through whereCompany() before passing to
deleteCustomers(), ensuring users cannot delete customers belonging
to other companies via the bulk delete endpoint.
2026-04-03 13:56:34 +02:00
Darko Gjorgjijoski
c469f64c79 Remove unused CompanySetting defaults from company setup
Remove invoice_auto_generate, payment_auto_generate, estimate_auto_generate
(defined but never read anywhere in the codebase) and duplicate save_pdf_to_disk
entries (now a global Setting, not per-company).
2026-04-03 10:36:48 +02:00
Darko Gjorgjijoski
9432da467e Add super-admin Administration section and restructure global vs company settings
- Add Administration sidebar section (super-admin only) with Companies, Users, and Global Settings pages
- Add super-admin middleware, controllers, and API routes under /api/v1/super-admin/
- Allow super-admins to manage all companies and users across tenants
- Add user impersonation with short-lived tokens, audit logging, and UI banner
- Move system-level settings (Mail, PDF, Backup, Update, File Disk) from per-company to Administration > Global Settings
- Convert save_pdf_to_disk from CompanySetting to global Setting
- Add per-company mail configuration overrides (optional, falls back to global)
- Add CompanyMailConfigService to apply company mail config before sending emails
2026-04-03 10:35:40 +02:00
Darko Gjorgjijoski
a38f09cf7b Installer reliability improvements (#593)
* docs: add CLAUDE.md for Claude Code guidance

* fix: handle missing settings table in installation middlewares

RedirectIfInstalled crashed with "no such table: settings" when the
database_created marker file existed but the database was empty.
Changed to use isDbCreated() which verifies actual tables, and added
try-catch around Setting queries in both middlewares.

* feat: pre-select database driver from env in installation wizard

The database step now reads DB_CONNECTION from the environment and
pre-selects the matching driver on load, including correct defaults
for hostname and port.

* feat: pre-select mail driver and config from env in installation wizard

The email step now fetches the current mail configuration on load
instead of hardcoding the driver to 'mail'. SMTP fields fall back
to Laravel config values from the environment.

* refactor: remove file-based DB marker in favor of direct DB checks

The database_created marker file was a second source of truth that
could drift out of sync with the actual database. InstallUtils now
checks the database directly via Schema::hasTable which is cached
per-request and handles all error cases gracefully.
2026-04-02 14:48:08 +02:00
mchev
aa88dc340d Closes #588 2026-04-01 21:30:32 +02:00
mchev
80889293bf Merge pull request #508 from alexdev01012020/email-backup
Overrite the email notification for backup
2026-03-27 08:04:02 +01:00
mchev
3bae2c282c Merge pull request #576 from csalzano/fix/remote-disk-backup-listing
Fix remote disk backups never appear in backup listing
2026-03-26 09:16:13 +01:00
Corey Salzano
14b5aaa0c9 Runs pint 2026-03-25 09:09:33 -04:00
Matthias Wirtz
71303a1050 Import File facade in UpdateCommand
Added use statement for File facade.
2026-03-24 08:03:53 +01:00
mchev
030c13b67a Merge pull request #578 from mchev/updates
Laravel 13 upgrade, security updates and fixes
2026-03-24 06:36:47 +01:00
mchev
14ccce1934 Merge pull request #572 from klittle81/payment-pdf-invoice-details
Update receipt PDF to include Invoice Total, Balance Due, and Invoice Status
2026-03-24 06:13:15 +01:00
mchev
07757e747e Addresses SSRF risk 2026-03-21 19:14:51 +01:00
mchev
c901114fc0 Pint 2026-03-21 18:59:53 +01:00
mchev
186ab35fd4 Laravel 13 upgrade, updates and fixes 2026-03-21 18:53:33 +01:00
Corey Salzano
3c31baf20d fix(backup): remote disk backups never appear in backup listing
Three bugs prevented backups stored on remote disks (Dropbox, S3, etc.)
from ever appearing in the Settings > Backup listing:

1. Typo in BackupSetting.vue: `filed_disk_id` was sent to the API
   instead of `file_disk_id`, so the backend never received the selected
   disk ID and always fell back to the local filesystem.

2. Wrong default disk selection in loadDisksData(): `set_as_default == 0`
   selected the first disk that is NOT default (local_public), instead of
   the disk that IS default. Changed to `set_as_default == 1` with a
   fallback to the first disk.

3. BackupsController::index() did not call setConfig() on the FileDisk
   before querying backups, so even when the correct file_disk_id arrived
   it still read from the default local filesystem. Added the same disk
   bootstrap logic already present in CreateBackupJob and destroy().

Made-with: Cursor
2026-03-17 19:32:40 -04:00
Corey Salzano
b35bd0880a Bug fix in Dropbox file disk creation. Applies disk path. 2026-03-03 11:47:55 -05:00
klittle81
fc05cf61fa Patch to update reciept PDF generated by payment.blade.php to include Invoice Total, Balance Due, and Invoice Status 2026-02-27 22:06:27 -05:00
Alex
a386fd19fc Merge remote-tracking branch 'upstream/master' into email-backup 2026-02-15 12:37:13 +02:00
Alex
e5f6984078 Apply review feedback 2026-02-06 16:17:57 +02:00
Abdulrazzaq Alhendi
65d1fdd3f0 feat(mail): add CC and BCC fields to email requests and forms (#466)
* feat(mail): add CC and BCC fields to email requests and forms

* chore: fmt
2026-02-06 01:59:38 +01:00
Devstack
af205acb75 Fix aggregates for customers using withSum() (SQL-portable, no DB mode tweaks) (#486)
* Update CustomersController.php

Fix: replace GROUP BY + SUM join with withSum() to avoid ONLY_FULL_GROUP_BY; no API changes (same aliases)

* Update CustomersController.php

style: apply Laravel Pint formatting
2026-01-01 22:43:52 +01:00
Alex
d2953e9409 Overrite the email notification for backup 2025-11-05 19:14:55 +02:00
Darko Gjorgjijoski
18d63a3375 Configurations cleanup & database configurations for mail and pdfs (#479)
* Move Mail, PDF configuration to Database, standardize configurations

* Set default currency to USD on install

* Pint code
2025-09-19 15:42:53 +02:00
Christos Yiakoumettis
3e96297699 Add expense number at Expenses (#406)
* add expense number at expenses

* Re-order expense fields

* Rename expense_number migration

* Add expense_number to tests

---------

Co-authored-by: Darko Gjorgjijoski <dg@darkog.com>
2025-09-02 03:20:27 +02:00
Darko Gjorgjijoski
f1635bcef8 Fix SQLite docker build related issues (#458) 2025-09-01 02:42:07 +02:00
Darko Gjorgjijoski
bae8dbe083 Upgrade mail configuration (#455)
* Upgrade the mail configuration

* Update mail configuration to match Laravel 12

* Update mail configuration to properly set none or null

* Pint code

* Upgrade Symfony Mailers
2025-08-31 03:04:31 +02:00
lupus
d5137e393d Fix: Use amounts in base currency for customer charts (#403)
Previously, the customer chart used the total/amount fields to calculate net profits/expenses/etc.
If the currency the expense (for example) was created in differed from the base currency of the company, the chart would display wrong amounts.
This change addresses the issue by always using the base currency field.
2025-08-30 12:30:49 +02:00
Darko Gjorgjijoski
a40bf5840d Dynamically load language files (#446) 2025-08-28 15:19:51 +02:00
Fabio Ribeiro
d69a56e2d5 feat: Tax included (#370)
* feat: Tax included

* Added a toggle switch in tax settings to enable the feature.
* Database migration adding tax_included field into estimates, invoices
  and recurring invoices table.
* Toggle switch to enable and store the tax_included by estimates,
  invoices and recurring invoices.
* In case of tax included enabled, total taxes will be recalculated and
  the invoices, estimates and recurring invoices total won't be sum with
  taxes.
* Apply tax included when discount_per_item/tax_per_item item is enabled.
* Custom component to show the net total when tax included is enabled.
* Update invoice and estimates pdfs with net total.

* chore: Tax included by default

A switch button inside the tax settings to enable the tax included by
default in invoices, estimates and recurring invoices.
2025-08-28 10:28:24 +02:00