Commit Graph

6 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
47907f9bf3 refactor(support): flatten Integrations umbrella and rename Formatters to Formatting
Two plural outliers were the only directories in Services/ and Support/ that didn't follow the singular naming convention we normalized in commit 947d00a9 (Documents -> Document).

**Integrations/ExchangeRate/ -> ExchangeRate/.** The Integrations/ umbrella was both plural AND introduced an extra layer of nesting no other Support subdir had (Pdf/, Hashids/, Module/, Update/ are all one level deep). Dropping the umbrella fixes both problems in one move and matches the existing shape. When AI providers eventually land, they follow the same pattern as Support/Pdf/: a sibling subdir at Support/Ai/, not buried under an umbrella.

**Formatters/ -> Formatting/.** Formatter (singular noun) would have been awkward; Formatting (gerund describing the capability) reads naturally as a namespace segment. The subdir holds DateFormatter, TimeFormatter, TimeZones — classes whose common thread is 'things that do formatting', which the gerund captures better than either the singular or plural noun form.

10 files renamed, 4 consumers updated (DriverRegistryProvider, ExchangeRateProviderService, FormatsController, ExchangeRateDriverFactoryTest). 350 tests pass, Pint clean.
2026-04-11 18:00:00 +02:00
Darko Gjorgjijoski
f657b53215 refactor(services): split driver infrastructure out of Services into Support
Services/Integrations/ExchangeRate/ and Services/Pdf/ were both mostly Support-shaped: interfaces, abstract classes, static factories, concrete adapter drivers, DTOs, and exceptions — infrastructure that doesn't carry business logic. They only each had one real DI-injected service mixed in.

This commit applies the same Services=DI-business-logic / Support=stateless-plumbing rule we've been using throughout the reorg:

**Moved to Support/Integrations/ExchangeRate/** (7 files): ExchangeRateDriver (abstract), ExchangeRateDriverFactory (static), ExchangeRateException, and the four concrete drivers (CurrencyConverter, CurrencyFreak, CurrencyLayer, OpenExchangeRate). These are HTTP adapters over third-party currency APIs — same shape as the Hashids library wrapper classes already in Support.

**Moved to Support/Pdf/** (6 files, merging with existing Pdf utilities): PdfDriver (interface), PdfDriverFactory (static), PdfService (static facade), GotenbergPdfDriver, GotenbergPdfResponse (DTO), ResponseStream (interface). The Support/Pdf/ dir now contains the full PDF rendering subsystem — drivers + sanitizer + template/image utilities.

**Promoted to Services/ root** (the real DI services): ExchangeRateProviderService (CRUD for ExchangeRateProvider model) and FontService (font package install/download orchestration). Both are proper DI services — instance methods, model writes, HTTP side effects.

Services/Integrations/ and Services/Pdf/ are now empty and deleted. Services/ holds only DI-injected classes; Support/ holds all the plumbing.

17 files renamed (git detects 90-99% similarity), 4 consumer files updated (DriverRegistryProvider, PdfServiceProvider, ExchangeRateProviderController, FontController, GeneratesPdfTrait, test). 350 tests pass, Pint clean.
2026-04-11 16:30:00 +02:00
Darko Gjorgjijoski
6d1816bd1b refactor: reorganize app/Services and app/Support by domain
The app/Services/ directory had grown into 22 flat files at the root plus 7 uneven subdirectories — finding anything required scrolling through an alphabetical mix of small CRUD services, infrastructure drivers, and install-time utilities. This commit groups services by domain, folds Backup into a new Storage namespace, and moves framework-infrastructure and install-time helpers out of Services and into Support where they belong.

New Services layout: Documents/ (Invoice, Estimate, RecurringInvoice, Payment, Expense, Transaction, DocumentItem, SerialNumber, Currency — matches the 'Documents' navigation group); Company/ (Company, Member, Invitation); Mail/ (MailConfiguration, CompanyMailConfig); Storage/ (FileDisk, plus Backup folded in). ExchangeRateProviderService moves next to its drivers in ExchangeRate/; FontService moves into Pdf/ where it belongs. CustomerService, ItemService, CustomFieldService stay at the Services root as standalone single-file domains.

Moves to Support/: Hashids/ (library wrapper — not business logic); Setup/ (one-shot install-time utilities — stateless helpers); Pdf/ (ImageUtils, PdfTemplateUtils, plus the existing PdfHtmlSanitizer consolidated into the same subdir). These are all framework infrastructure and stateless utilities — the 'service' label never really fit them.

Namespace declarations in 29 moved files updated to match new paths. 62 consumer files (controllers, other services, tests, database factories, seeders, routes, bootstrap/providers.php) have their use statements rewritten via a literal-string replacement script — no regex meant no risk of half-matching. Three Documents services needed an explicit 'use App\Services\Mail\CompanyMailConfigService' added because the same-namespace short reference they relied on no longer resolves after the split.

Verified: composer dump-autoload, 350 tests pass (850 assertions), vendor/bin/pint clean, npm run build succeeds.
2026-04-11 10:00:00 +02:00
Darko Gjorgjijoski
b0b7d40c73 Fix exchange rate parity across all document types
- Fix exchange-rate service types to match actual backend response shapes
  (exchangeRate array, activeProvider success/error, used currencies as strings)
- Add ExchangeRateConverter to payments, expenses, and recurring invoices
- Set currency_id from customer currency in invoice/estimate selectCustomer()
- Load globalStore.currencies in ExchangeRateConverter on mount
- Pass driver/key/driver_config params to getSupportedCurrencies in provider modal
- Fix OpenExchangeRateDriver validateConnection to use base=USD (free plan compat)
- Fix checkActiveCurrencies SQLite whereJsonContains with array values
- Remove broken currency/companyCurrency props from ExpenseCreateView, use stores
- Show base currency equivalent in document line items and totals when exchange
  rate is active
2026-04-06 21:07:50 +02:00
Darko Gjorgjijoski
85b62dfdf8 Refactor exchange rate providers into driver-based architecture
Replace duplicated switch/case blocks across 4 methods with a clean
abstract driver pattern:

- ExchangeRateDriver (abstract): defines getExchangeRate(),
  getSupportedCurrencies(), validateConnection()
- CurrencyFreakDriver, CurrencyLayerDriver, OpenExchangeRateDriver,
  CurrencyConverterDriver: concrete implementations
- ExchangeRateDriverFactory: resolves driver name to class, with
  register() method for module extensibility

Delete ExchangeRateProvidersTrait — all logic now lives in driver
classes and ExchangeRateProviderService. Adding a new exchange rate
provider only requires implementing ExchangeRateDriver and calling
ExchangeRateDriverFactory::register() in a module service provider.
2026-04-03 20:24:03 +02:00
Darko Gjorgjijoski
8f29e8f5de Extract business logic from remaining models to services
New services:
- ExchangeRateProviderService: CRUD, API status checks, currency converter
  URL resolution (extracted 122 lines from ExchangeRateProvider model)
- FileDiskService: create, update, setAsDefault, validateCredentials
  (extracted 97 lines from FileDisk model)
- ItemService: create/update with tax handling (extracted from Item model)
- TransactionService: create/complete/fail (extracted from Transaction model)
- CustomFieldService: create/update with slug generation (extracted from
  CustomField model)

Controllers updated to use constructor-injected services:
ExchangeRateProviderController, DiskController, ItemsController,
CustomFieldsController.
2026-04-03 19:32:37 +02:00