Commit Graph

120 Commits

Author SHA1 Message Date
Darko Gjorgjijoski
a2aa74aa8a fix(migrations): align ai_conversations company_id/user_id with INT pk (#683)
Follow-up to #618. The ai_conversations table (added after #618) used
foreignId() (BIGINT) for company_id/user_id, which mismatches the INT
UNSIGNED users.id / companies.id and breaks MySQL FK creation (error 3780)
on the v2->v3 upgrade. Use plain unsignedInteger, matching the codebase's
no-DB-FK convention for these columns. conversation_id stays foreignId — it
references the BIGINT ai_conversations.id and its delete cascade is intentional
(and covered by AiChatFlowTest).
2026-06-12 15:19:21 +02:00
mchev
acbb1f040c fix(migrations): align FK column types with users/companies id on MySQL (#618)
Laravel foreignId() creates BIGINT UNSIGNED columns, but users.id and
companies.id use increments() (INT UNSIGNED). MySQL 8 rejects foreign keys
when referencing and referenced column types differ (error 3780).

Use unsignedInteger for impersonation_logs admin_id/user_id and for
company_invitations company_id, user_id, and invited_by. Keep foreignId
for role_id since roles.id is bigIncrements.

This fixes upgrades from v2 on MySQL when running v3.0 migrations.

Made-with: Cursor
2026-06-12 14:43:27 +02:00
Darko Gjorgjijoski
e861fc1fc1 feat(ai): Phase 2 — chat assistant with tool-calling
Second phase of the AI feature. Users can now open a slide-in chat drawer from the SiteHeader and ask natural-language questions about their company's invoices, customers, payments, and expenses. The LLM invokes pre-defined read-only tool functions (scoped to the current company at execute time) to fetch data and synthesize answers.

**Database** — new ai_conversations and ai_messages tables. Messages are stored in OpenAI's chat format so AiAssistantService serializes a conversation into an API request with zero translation. Columns: role, content, tool_call_id, tool_calls JSON, model, tokens_in, tokens_out. Conversations are scoped (company_id, user_id) — one user's chats are invisible to everyone else, even inside the same company. Foreign-key cascade deletes.

**Tool infrastructure** — AiTool abstract base + AiToolRegistry singleton (registered in a new AiServiceProvider). The base class enforces the security rule: every tool's execute() receives companyId and userId as injected parameters; tools' JSON schemas NEVER include a company_id field. An LLM physically cannot pass a company_id and escape tenancy. Modules can register their own tools by resolving the registry from their own ServiceProvider::boot().

**Nine built-in tools**: search_invoices, get_invoice, search_customers, get_customer, list_recent_payments, list_overdue_invoices, get_company_stats (aggregates for named periods), search_items, list_expense_categories. All read-only; no mutations. Each returns JSON-encodable data the LLM can parse.

**AiAssistantService orchestration loop** — the heart of Phase 2. Flow: persist user message → build payload from system prompt + recent history (40-message window) + new user message → call driver.chatCompletion with tools → if tool_calls, execute each one via the registry (with injected scope), persist tool result, loop → if plain text, persist and return. Hard cap at 5 iterations to prevent runaway LLMs. System prompt pins the assistant to this company's data and forbids mutation.

**Controllers + policy + rate limit** — POST /api/v1/ai/chat runs the orchestration loop. GET/PATCH/DELETE /api/v1/ai/conversations for CRUD. AiConversationPolicy enforces user_id+company_id match on every action. A new 'ai' RateLimiter in RouteServiceProvider throttles to 30 req/min per (user, company). New 'use ai' Gate defined in AppServiceProvider returns true for any authenticated user — the per-company kill-switch still goes through AiConfigurationService::resolveForCompany.

**Frontend** — new features/company/ai/ folder with a Pinia store (ai-chat.store.ts) holding drawer state, current conversation, messages, and loading flags. AiChatDrawer.vue is a slide-in panel teleported to <body>, mounted globally in CompanyLayout.vue when bootstrap reports ai.enabled && ai.chat_enabled. Sub-components: AiChatMessage (user bubbles vs assistant bubbles), AiChatMessageInput (Enter submits, Shift+Enter newline), AiChatConversationList (sidebar with 'new chat' button, rename, delete). A SparklesIcon button in SiteHeader toggles the drawer.

**Driver test double** — tests use a ScriptedAiDriver registered via AiDriverFactory::register('scripted', ...) that returns pre-queued AiChatResponse objects. Feature tests cover: happy path (new conversation + message persistence), tool-call loop (multi-round-trip with search_invoices), runaway-loop cap, driver-throws path, ai_enabled=NO rejection, chat role disabled rejection, per-user conversation visibility, cross-user policy enforcement, cascade delete.

388 tests pass (was 372, +16 new). Pint clean. npm run build clean. Phase 3 (WYSIWYG text generation popup) is the remaining follow-up.
2026-04-12 08:00:00 +02:00
Darko Gjorgjijoski
2af31d0e5f Rename local_public disk back to public and store avatars/logos on public disk
The public disk was accidentally removed during the Laravel 11 upgrade
and re-added as local_public in the FileDisk refactor. Restoring the
standard Laravel name avoids breaking Spatie MediaLibrary expectations
and simplifies the v2-to-v3 upgrade path.

User avatars and company logos now explicitly use the public disk via
registerMediaCollections(), keeping them web-accessible while the default
media disk remains private for sensitive documents like PDFs and receipts.

The v3 upgrade migration renames the system disk entry and any alpha media
records from local_public back to public.
2026-04-10 15:56:31 +02:00
Darko Gjorgjijoski
5efd1054f4 Add v3.0 upgrade migration
Migrates media disk references from the old temp_{driver} naming to
the new disk_{id} scheme. System local disks map to 'local', remote
disks map to 'disk_{id}'. Structured as the single v3.0 upgrade
migration for future additions.
2026-04-07 02:12:38 +02:00
Darko Gjorgjijoski
92a1baced4 Add company invitation system (backend)
New feature allowing company owners/admins to invite users by email with
a specific company-scoped role.

Database:
- New company_invitations table (company_id, email, role_id, token,
  status, invited_by, expires_at)

Backend:
- CompanyInvitation model with pending/forUser scopes
- InvitationService: invite, accept, decline, getPendingForUser
- CompanyInvitationMail with markdown email template
- InvitationController (company-scoped): list, send, cancel invitations
- InvitationResponseController (user-scoped): pending, accept, decline
- BootstrapController returns pending_invitations in response
- CompanyMiddleware handles zero-company users gracefully

Tests: 9 feature tests covering invite, accept, decline, cancel, expire,
duplicate prevention, and bootstrap integration.
2026-04-03 22:58:55 +02:00
Darko Gjorgjijoski
00d5abae5f Eliminate Company\CompaniesController, introduce owner role
Redistribute methods:
- show() -> BootstrapController::currentCompany()
- store(), destroy(), userCompanies() -> Admin\CompaniesController
- transferOwnership() -> CompanySettingsController

Security fix: introduce 'owner' role for company-level admin, distinct
from 'super admin' which is now global platform admin only.
- CompanyService::setupRoles() creates 'owner' role per company
- Company creation assigns scoped 'owner' role instead of global 'super admin'
- Seeders updated to assign 'owner'

Migration renames all existing company-scoped 'super admin' roles to
'owner' and ensures every company owner has the role assigned.
2026-04-03 22:33:56 +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
mchev
c901114fc0 Pint 2026-03-21 18:59:53 +01:00
Ahmed Ashraf
24546aea3c New currency: Qatari Riyal (#476)
* add qatari riyal currency to migration and seeder

* run pint
2026-01-01 17:50:31 +01: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
32f7bc053a New currencies: Paraguayan Guaraní, Algerian Dinar (#447)
* feat(currency): add Algerian Dinar (DZD) support (#395)

* Add Paraguayan Guarany (PYG) currency  (#434)

* Adding Paraguayan currency, closes #404

* Adding the currency to the seeder too

* If the data was already seeded, don't add the entry

* Pint

---------

Co-authored-by: Darko Gjorgjijoski <5760249+gdarko@users.noreply.github.com>

* Add DZD currency to the currencies seeder

---------

Co-authored-by: Polat İnceler <inceler.polat@gmail.com>
Co-authored-by: mchev <martin.chevignard@gmail.com>
2025-08-28 14:13:03 +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
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
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
mchev
1bb65f420c Fix negative values on item price (#335)
* Fix negative values on item price

* Remove console log
2025-04-05 00:43:34 +02:00
Darko Gjorgjijoski
e9e52c60a7 Reformat with pint 2025-01-12 18:37:08 +01: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
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
Darko Gjorgjijoski
53343c64eb Update migrations formatting & namespace 2024-07-28 23:14:34 +02:00
Darko Gjorgjijoski
dfc4b1982e Update namespace on 1.x migration 2024-07-28 23:13:53 +02:00
Darko Gjorgjijoski
8dd9d0c414 Consolidate migrations v2.0 should go after v1.x 2024-07-28 22:46:45 +02:00
Darko Gjorgjijoski
84a6181240 Merge master 2024-07-28 22:43:02 +02:00
mchev
c8439d859d Add cache and queue tables migrations (#121)
* Add cache and queue tables migrations
2024-07-17 12:06:04 +02:00
Darko Gjorgjijoski
27a511bc09 Update version
[ci-skip]
2024-07-13 03:11:14 +02:00
Darko Gjorgjijoski
98852686c7 Add doctrine/dbal and run pint 2024-07-13 02:03:22 +02:00
mchev
210f735e40 Adding new field to update #107 2024-06-16 21:35:55 +02:00
mchev
a0bf6f7f59 Replacing model_type in database (#100)
* Replacing model_type in database

* Also update old Crater namespace
2024-06-07 12:10:57 +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
Darko Gjorgjijoski
1e530e0387 Include lang directory in the dist build (#65)
* Include lang/ dir in the build

* Bump version
2024-05-04 15:27:40 +02:00
Darko Gjorgjijoski
fa777d673f Update release migration rollback to point to 1.2.0 2024-05-04 13:48:42 +02:00
Darko Gjorgjijoski
8570252db5 Add missing migration 2024-05-04 13:35:51 +02:00
Timo
2784318ebf Migrate polymorphic relationships (#58)
* add crater media model type migration

* migrate all morph type fields

* fix code style

* migrate custom_field_values
2024-04-21 00:03:34 +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
Darius Kristapavicius
5ba8639ac8 Added expires_at column to personal_access_tokens table 2024-02-17 23:51:21 +02:00
gdarko
44153c2993 Improve 110 migration rollback 2024-02-11 20:43:03 +01:00
gdarko
b9d5b8247c Fix: Wrong migration name 2024-02-11 20:42:01 +01:00
gdarko
d2ad2222be Fix code style 2024-02-11 09:41:23 +01:00
gdarko
2358969050 Add release migration and fix some old database leftovers 2024-02-11 09:38:21 +01:00
Daniel Rodríguez
b819440e76 feat: allow negative taxes 2024-02-08 19:19:32 +01:00
Darko Gjorgjijoski
aa68332cf6 Improve installed database detection 2024-02-04 19:37:09 +01:00
gdarko
4ab92473e9 Setup pint & run code style fix 2024-01-29 04:46:01 -06:00
gdarko
fdb46275c0 Fix database migration rollback 2021-11-06 14:22:59 -05:00
gdarko
e43ddeea54 Fix wrong rollback in add_overdue_to_invoices_table migration 2024-01-28 15:11:58 -06:00
Darko Gjorgjijoski
cd9df54c5b Upgrade to Laravel 10, Vite 5+ 2024-01-28 17:17:32 +01:00
Darko Gjorgjijoski
6b80b5f48d Change namespace 2024-01-27 23:53:20 +01:00
Mohit Panjwani
18507ddb6f new build 606 2022-03-06 12:39:21 +05:30
harshjagad20
fadef0ea07 Fix tax per item issue & check currency key 2022-03-04 12:08:03 +05:30
harshjagad20
69d8c95557 Minor fixes 2022-03-03 13:25:12 +05:30