Commit Graph

62 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
d3202b8b2a fix(members): scope member view & update to the acting company
Member view/update bound the target user by global id and authorized only that the requester owns their active company, not that the target belonged to it. Bind the route model under the members param and require shared company membership in UserPolicy so an owner of one company can no longer read or modify users of another.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 23:42:11 +02:00
Darko Gjorgjijoski
e41ee8083d fix(documents): preserve edited line-item description (#658)
The per-line item description typed into the row's textarea was never
saved. BaseItemSelect's description field pushes edits out only via an
`update:description` emit, but DocumentItemRow (the single shared row used
by invoices, estimates, and recurring invoices) wired only `@search` and
`@select` — so the emit was dropped and `form.items[index].description`
never updated. On submit the field was lost; a catalog item's description
also reverted on the next re-render.

Capture the event and route it through the existing `updateItemAttribute`
store updater, mirroring how name/quantity/price/discount already sync.
One fix covers all three document types since they share the row.

Also add the missing `items.*.description` nullable rule to
RecurringInvoiceRequest for parity with the invoice/estimate requests.

The backend already persisted and returned description correctly; this was
purely a dropped frontend event.
2026-06-12 11:27:56 +02:00
Darko Gjorgjijoski
84524ce247 fix(security): recompute document totals server-side (GHSA-8c69) (#672)
v3 port. Invoice/estimate/recurring creation and update accepted total,
sub_total, tax and due_amount straight from the request with no recalculation,
letting a client persist financial totals that don't match the line items
(and corrupt the invoice update due-amount/paid-amount logic which keyed off
the client total).

- Adds App\Support\DocumentTotals (mirrors the front-end calc) trusting only
  price/quantity/discounts/tax-line amounts.
- getInvoicePayload/getEstimatePayload/getRecurringInvoicePayload override the
  client totals; the shared DocumentItemService::createItems recomputes each
  item total; InvoiceService::update keys its due-amount logic off the
  recomputed total.

Adds DocumentTotals unit tests + a feature test proving a tampered invoice
total is ignored; existing create/update tests no longer assert the now
server-authoritative derived totals.
2026-06-12 11:02:23 +02:00
Darko Gjorgjijoski
99ea898e88 fix(security): block SSRF via the Gotenberg host setting (GHSA-mfxg) (#671)
v3 port. The Gotenberg PDF driver was missed when the SSRF guards were added
to the AI, exchange-rate and file-disk drivers: gotenberg_host was validated
only with 'url', and the driver POSTs the rendered HTML to it.

Reuses the existing infrastructure (consistency with the other drivers):
- Wires App\Rules\PublicHttpUrl into the gotenberg_host validation rule.
- Adds PrivateNetworkGuard::assertAllowed() in GotenbergPdfDriver before the
  outbound call (covers env/seed/stale config + DNS rebinding).

Adds a unit test asserting the gotenberg_host rule rejects private/loopback/
link-local addresses and allows a public one.
2026-06-12 11:02:19 +02:00
Darko Gjorgjijoski
ac2a8ca939 fix(security): gate AI tools by user ability and block admin-URL SSRF
The AI chat assistant scoped tool queries by company but ignored the
per-user Bouncer abilities the rest of the app enforces, so any `use ai`
holder could read customers, invoices, payments, and company financials
their role couldn't otherwise see. Each AiTool now declares a required
ability (entity-aligned); the registry hides unauthorized tools from the
model and refuses to execute them as a backstop.

Separately, admin/owner-supplied URLs were fetched server-side with no
guard against private/reserved targets (SSRF): the AI base URL, the
CurrencyConverter "DEDICATED" exchange-rate URL, and S3/Spaces file-disk
endpoints. A shared PrivateNetworkGuard now backs a PublicHttpUrl
validation rule (save-time) and runtime guards in each driver.

- AiTool::requiredAbility() + mapping across all 12 tools
- AiToolRegistry filters schemas() by ability and re-checks in execute()
- PrivateNetworkGuard / BlockedUrlException / PublicHttpUrl rule (new)
- Rule wired into AI config (service + 3 controllers), exchange-rate,
  and file-disk endpoints; runtime guards in OpenRouterDriver,
  CurrencyConverterDriver, and FileDiskService
- Tests for ability filtering, the guard, the rule, and 422 rejections
2026-06-05 00:01:09 +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
23d1476870 refactor(modules): marketplace install flow with checksum validation
Rewires module installation to use slug + version + checksum_sha256 instead of the opaque module identifier. ModuleInstaller splits marketplace token handling out of install() into helpers, adopts structured error responses, and validates the downloaded archive's SHA-256 against the marketplace manifest before unpacking.

ModuleResource is simplified to accept an already-loaded installed-module instance rather than fetching it from state, exposes access_tier and checksum fields, and drops the auto-disable-on-unpurchased side effect that was bleeding write logic into a read resource. UnzipUpdateRequest accepts a nullable module with a conditional module_name field so the same endpoint serves both app and module updates.

ModulesPolicy::manageModules now short-circuits for super-admins so administration flows (token validation, store state) are not blocked on a company-scoped ability. Two new feature tests cover both the authorization bypass and ModuleResource serialization.
2026-04-10 17:30:00 +02:00
Darko Gjorgjijoski
9174254165 Refactor install wizard and mail configuration 2026-04-09 10:06:27 +02:00
Darko Gjorgjijoski
cb37de6da4 Auto-generate company slug server-side in CompanyRequest
CompanyRequest::getCompanyPayload() accepted 'slug' from the client but never generated it, so the installation wizard (which PUTs /api/v1/company) left the slug empty when setting up the first company. Match the sibling CompaniesRequest (which already does Str::slug($this->name)) and generate the slug from the name server-side; drop the now-unused 'slug' validation rule.

Fixes the same bug that master's ed7af3fc tried to fix client-side with a lodash deburr + regex workaround in Step7CompanyInfo.vue. v3.0's installation wizard is a rewrite under resources/scripts/features/installation/CompanyView.vue and doesn't carry that workaround, so the cleaner fix is to make the backend authoritative like CompaniesRequest already is.
2026-04-07 17:30:34 +02:00
Darko Gjorgjijoski
39c9179888 Support internationalized domain names (IDN) in email validation
Add IdnEmail validation rule that converts IDN domains to Punycode
via idn_to_ascii() before validating with FILTER_VALIDATE_EMAIL.
Applied to all email fields: customers, members, profiles, admin
users, customer portal profiles, and mail configuration.

Includes unit tests for standard emails, IDN emails, and invalid
inputs.

Fixes #388
2026-04-06 23:55:29 +02:00
Darko Gjorgjijoski
8a6c085288 Rename company-scoped Users to Members throughout
Complete rename across backend and frontend:
- Controller: Company/Users/UsersController -> Company/Members/MembersController
- Service: UserService -> MemberService
- Requests: UserRequest -> MemberRequest, DeleteUserRequest -> DeleteMemberRequest
- API routes: /api/v1/users -> /api/v1/members (company-scoped only)
- Sidebar menu: "Users" -> "Members"
- Frontend: views/users -> views/members, stores/users -> stores/members
- Router: users.index -> members.index, /admin/users -> /admin/members
- i18n: new "members" section with invitation-related keys
- Tests: UserTest -> MemberTest

Admin/super-admin Users (system-wide user management) remains unchanged.
2026-04-03 23:12:30 +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
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
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
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
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
mchev
bf5b544ca3 Adding Flat Tax support with fixed amount (#253)
* Possibility to set a fixed amount on tax types settings

* Pint and manage flat taxes on items

* Fix display errors and handle global taxes

* Tests

* Pint with PHP 8.2 cause with PHP 8.3 version it cause workflow error

* Merging percent and fixed amount into one column

* Now display the currency on SelectTaxPopup on fixed taxes
2025-05-04 02:24:56 +02:00
Tim van Osch
bf40f792c2 Feat(Gotenberg): Opt-in alternative pdf generation for modern CSS (#184)
* WIP(gotenberg): add pdf generation abstraction and UI

* feat(pdf): settings validate(clien+server) & save

* fix(gotenberg): Use correct default papersize
chore(gotengberg): Remove unused GOTENBERG_MARGINS env from .env

* style(gotenberg): fix linter/styling issues

* fix(pdf): use pdf config policy

* fix: revert accidental capitalization in mail config vue

* Update composer, remove whitespace typo

* Fix small typos

* fix cookie/env issue

* Add gotenberg to .dev, move admin menu item up
2025-05-04 02:10:15 +02:00
Yannic Inselmann
b32c334a71 feat: default notes (#263)
* feat: default notes

* feat: include default invoice note in recurring invoice

* feat: use default export in tw config

* fix: test and naming

* fix: consistent ui for switch in note modal

* feat: little text improvements
2025-04-05 12:01:06 +02:00
Darko Gjorgjijoski
f52b73f517 Invoice time support (#269)
* Changed invoice date to datetime

* Fixed code style errors

* Update TimeFormatsController.php

* Update TimeFormatter.php

* Update TimeFormatsController namespace

* Fix missing comma in language file

* Fix formatting

---------

Co-authored-by: troky <troky2001@yahoo.com>
2025-01-12 13:32:47 +01:00
mchev
ef9adbd6c9 Adding sent and viewed field to payload (#247) 2025-01-12 11:27:29 +01:00
mchev
967c225df9 Merge pull request #198 from mchev/invoice_cancellation
Support for Zero and Negative Item Quantities on Invoices
2024-11-02 12:20:55 +01:00
Martin Chevignard
03b9defeb1 Customers tax id field 2024-10-04 12:07:29 +02:00
mchev
14c599ed4f Allow decimals on items quantities (#94)
* Allow decimals on items quantities #80

* Updating all requests validation

* Revert discount_val
2024-06-05 15:10:36 +02:00
mchev
bf5a8cdb28 Increasing base total validation limit for IDR currency (#98) 2024-06-05 12:16:09 +02:00
mchev
3259173066 Laravel 11 (#84)
* Convert string references to `::class`

PHP 5.5.9 adds the new static `class` property which provides the fully qualified class name. This is preferred over using strings for class names since the `class` property references are checked by PHP.

* Use Faker methods

Accessing Faker properties was deprecated in Faker 1.14.

* Convert route options to fluent methods

Laravel 8 adopts the tuple syntax for controller actions. Since the old options array is incompatible with this syntax, Shift converted them to use modern, fluent methods.

* Adopt class based routes

* Remove default `app` files

* Shift core files

* Streamline config files

* Set new `ENV` variables

* Default new `bootstrap/app.php`

* Re-register HTTP middleware

* Consolidate service providers

* Re-register service providers

* Re-register routes

* Re-register scheduled commands

* Bump Composer dependencies

* Use `<env>` tags for configuration

`<env>` tags have a lower precedence than system environment variables making it easier to overwrite PHPUnit configuration values in additional environments, such a CI.

Review this blog post for more details on configuration precedence when testing Laravel: https://jasonmccreary.me/articles/laravel-testing-configuration-precedence/

* Adopt anonymous migrations

* Rename `password_resets` table

* Convert `$casts` property to method

* Adopt Laravel type hints

* Mark base controller as `abstract`

* Remove `CreatesApplication` testing trait

* Shift cleanup

* Fix shift first issues

* Updating Rules for laravel 11, sanctum config and pint

* Fix Carbon issue on dashboard

* Temporary fix for tests while migration is issue fixed on laravel side

* Carbon needs numerical values, not strings

* Minimum php version

* Fix domain installation step not fetching the correct company_id

* Fix Role Policy wasn't properly registered

---------
2024-06-05 11:33:52 +02:00
Timo
8c83df558c Add Company VAT-ID and Tax-ID (#54)
* add company vat_id & tax_id field

* add tax & vat id field in company settings

* fix vat & tax id validation

* add german vat & tax id translation

* add translations for pdf

* add vat_id and tax_id field before timestamps

* make fields nullable and fix code style
2024-04-20 23:08:32 +02:00
Darko Gjorgjijoski
8788f3d504 Tax calculation issue (#38)
* fix initial tax per item issue

* remove commit in estimate storage

* add changes in tax per item calculation

* add validation on requests

* fix minimum total issue

* fix table pagination filter issue

* minor fix

* remove compound interest and remove unused code

---------

Co-authored-by: yashkanakiya <yashkanakiya281297@gmail.com>
Co-authored-by: dhruvbhattt <dhruvbhatt7790@gmail.com>
Co-authored-by: gdarko <dg@darkog.com>
2024-02-18 10:54:12 +01:00
gdarko
4ab92473e9 Setup pint & run code style fix 2024-01-29 04:46:01 -06:00
Darko Gjorgjijoski
6b80b5f48d Change namespace 2024-01-27 23:53:20 +01:00
theWorstComrade
7cde971f8b Module upload validation (#857)
https://huntr.dev/bounties/cb9a0393-be34-4021-a06c-00c7791c7622/
2022-03-29 12:55:35 +05:30
theWorstComrade
88035ea490 Expense attachment validation fix (#855)
https://huntr.dev/bounties/4d7d4fc9-e0cf-42d3-b89c-6ea57a769045/
2022-03-22 16:58:55 +05:30
Mohit Panjwani
b781f11175 Merge branch 'master' of https://github.com/bytefury/crater 2022-02-10 11:50:13 +05:30
avinashrathod
c82dc94252 fix receipt not uploading in expense 2022-02-03 11:39:18 +05:30
theWorstComrade
ff3cd0f7b9 Customer avatar validation (#732)
* Customer avatar validation

https://huntr.dev/bounties/19f3e5f7-b419-44b1-9c37-7e4404cbec94/

* Customer avatar validation test

https://huntr.dev/bounties/19f3e5f7-b419-44b1-9c37-7e4404cbec94/
2022-01-19 17:08:34 +05:30
Mohit Panjwani
bdea879273 v6 update 2022-01-10 16:06:17 +05:30
Mohit Panjwani
8d94f4efbe fix code style 2021-12-01 16:18:27 +05:30
harshjagad20
88bfb38b56 solve unit tests 2021-12-01 13:25:24 +05:30
Mohit Panjwani
082d5cacf2 v5.0.0 update 2021-11-30 18:58:19 +05:30
gohil jayvirsinh
d1dd704cdf Add File based templates 2021-06-19 12:11:21 +00:00
Raish
8eb47b553e Add Domain Verification on Onboarding Wizard 2021-06-18 09:51:19 +00:00
Mwikala Kangwa
9e98a96d61 Implement PHP CS Fixer and a coding standard to follow (#471)
* Create PHP CS Fixer config and add to CI workflow

* Run php cs fixer on project

* Add newline at end of file

* Update to use PHP CS Fixer v3

* Run v3 config on project

* Run seperate config in CI
2021-05-21 17:27:51 +05:30
raishvaria
109de237b1 update: SMTP username & password not mandatory in mail configuraiton 2021-04-10 12:56:09 +05:30
Mohit Panjwani
89ee58590c build version 400 2020-12-02 17:54:08 +05:30
bansarishukla
d79692cf3b mail configuration issue 2020-01-07 12:33:03 +05:30
Jay Makwana
4c33a5d88c Fix Invoice/Estimate template issues and Add Payment Receipt, Custom Payment Modes and Item units 2020-01-05 07:22:36 +00:00
raishvaria
75ddc51b1e fix installation issue & build 210 2019-12-14 18:50:13 +05:30
Mohit Panjwani
7df06fb005 fix invoice number validation 2019-12-14 12:35:32 +05:30
Mohit Panjwani
18ad27d513 Merge branch 'customer' into 'master'
Fix customer email already exist validation when edit customer

See merge request mohit.panjvani/crater-web!71
2019-11-23 06:42:41 +00:00