Commit inicial - upload de todos os arquivos da pasta

This commit is contained in:
2026-07-24 23:55:21 -03:00
commit 9d4d395881
88 changed files with 9484 additions and 0 deletions
@@ -0,0 +1,113 @@
## ADDED Requirements
### Requirement: Categoria Table Schema
The system SHALL provide a `categoria` table with fields `id`, `categoria`, and `palavra_chave`, used to map keywords to category names as a fallback for automatic classification. The row with `id = 1` is reserved by the system for the "Não Encontrado" category and SHALL always exist; the system SHALL ensure this row is present (creating it if missing) during schema initialization, independent of any other categories the user registers.
#### Scenario: Table created on schema initialization
- **WHEN** the application initializes or migrates the database schema
- **THEN** the `categoria` table exists with columns `id`, `categoria`, `palavra_chave`, and a row with `id = 1` and `categoria = "Não Encontrado"` is present
#### Scenario: Reserved row already present
- **WHEN** the application initializes and a `categoria` row with `id = 1` already exists (e.g., the user has customized its `categoria`/`palavra_chave` values)
- **THEN** the system does not overwrite or duplicate that row
### Requirement: Automatic Categorization on Ingestion
The system SHALL attempt to determine the category of a fiscal document automatically at the moment it is confirmed/created, without requiring manual user input.
#### Scenario: New fiscal document confirmed triggers categorization
- **WHEN** a detected document is confirmed and a `fiscal_documents` row is created
- **THEN** the system runs the categorization flow (AI, then keyword fallback) before finishing the request
### Requirement: AI-Based Categorization by Supplier Name
The system SHALL first attempt to categorize a fiscal document by sending the supplier name to the AI service and requesting a category classification.
#### Scenario: AI returns a valid category
- **WHEN** the AI service returns a recognized, non-empty category for the supplier name
- **THEN** the system stores that category on the fiscal document and does not consult the `categoria` keyword table
#### Scenario: AI call fails or returns no usable category
- **WHEN** the AI call raises an error, times out, or returns an empty/unrecognized value
- **THEN** the system proceeds to keyword fallback categorization instead of failing the request
### Requirement: Keyword Fallback Categorization
When AI categorization does not produce a valid category, the system SHALL search the `categoria` table for a `palavra_chave` that matches (case-insensitive, substring) the supplier name, and use the corresponding `categoria` value.
#### Scenario: Keyword match found
- **WHEN** AI categorization did not yield a category and a `categoria.palavra_chave` is found as a substring of the supplier name (case-insensitive)
- **THEN** the system assigns the matching `categoria.categoria` value to the fiscal document
#### Scenario: No keyword match found
- **WHEN** AI categorization did not yield a category and no `palavra_chave` matches the supplier name
- **THEN** the system assigns `categoria_id = 1` ("Não Encontrado") to the fiscal document instead of guessing
### Requirement: Fallback to Reserved "Não Encontrado" Category
When neither AI nor keyword matching determines a category for a fiscal document being processed by the categorization flow, the system SHALL persist that document with `categoria_id = 1` (the reserved "Não Encontrado" category) and SHALL NOT block or fail document processing.
#### Scenario: No category determined by any method
- **WHEN** AI categorization fails/is inconclusive and no keyword match exists for a document going through the categorization flow
- **THEN** the fiscal document is saved successfully with `categoria_id = 1` and shown as "Não Encontrado" in the UI
#### Scenario: Legacy documents predating this change remain distinct from "Não Encontrado"
- **WHEN** a `fiscal_documents` row was created before this change and has never been run through the categorization flow
- **THEN** its `categoria_id` remains `NULL` and it is shown as "Sem categoria" (distinct from the explicit "Não Encontrado" outcome), until it is reprocessed
### Requirement: Dashboard Category Breakdown
The Dashboard SHALL display expenses aggregated by category (e.g., totals per category) alongside existing period and supplier breakdowns.
#### Scenario: Dashboard shows category totals
- **WHEN** a user opens the Dashboard
- **THEN** the page displays total spend grouped by category, including the "Não Encontrado" group (`categoria_id = 1`) for documents the categorization flow could not classify, and a separate "Sem categoria" group for any legacy documents with `categoria_id` still `NULL`
### Requirement: Dashboard Category Filter
The Dashboard SHALL allow the user to filter the displayed expenses by a single selected category.
#### Scenario: User filters by category
- **WHEN** the user selects a category from the Dashboard's category filter
- **THEN** all Dashboard figures (KPIs, charts, recent documents) update to reflect only fiscal documents in that category
#### Scenario: User clears the category filter
- **WHEN** the user clears the selected category filter
- **THEN** the Dashboard returns to showing all fiscal documents regardless of category
### Requirement: Categoria Administration List
The system SHALL provide an authenticated admin page listing all rows of the `categoria` table (`id`, `categoria`, `palavra_chave`), following the same page layout, table markup, and CSS classes already used by the existing "Documentos" list page.
#### Scenario: User views the categoria list
- **WHEN** an authenticated user navigates to the categorias admin page
- **THEN** the system displays all `categoria` rows in a table matching the existing list-page layout (`card`/`table`/`table-scroll` styling), with actions to create, edit, and delete a row
### Requirement: Categoria Creation
The system SHALL allow an authenticated user to create a new `categoria` row (`categoria`, `palavra_chave`) via a form that follows the same structure, validation, and CSRF protection as the existing document create/edit form.
#### Scenario: User creates a new categoria
- **WHEN** an authenticated user submits the "new categoria" form with a non-empty `categoria` and `palavra_chave`
- **THEN** the system inserts a new row into the `categoria` table and redirects to the categoria list showing the new entry
#### Scenario: User submits an invalid categoria form
- **WHEN** an authenticated user submits the "new categoria" form with a missing `categoria` or `palavra_chave`
- **THEN** the system re-displays the form with a validation error and does not create a row
### Requirement: Categoria Update
The system SHALL allow an authenticated user to edit an existing `categoria` row's `categoria` and `palavra_chave` values, reusing the same form template pattern used for creation (single form, `mode` toggling between new/edit).
#### Scenario: User edits an existing categoria
- **WHEN** an authenticated user submits the edit form for an existing `categoria` row with valid values
- **THEN** the system updates that row and redirects to the categoria list reflecting the new values
### Requirement: Categoria Deletion
The system SHALL allow an authenticated user to delete an existing `categoria` row (other than the reserved `id = 1` row) via a CSRF-protected POST action, following the same inline delete-form-with-confirmation pattern used on the existing "Documentos" list page.
#### Scenario: User deletes a categoria
- **WHEN** an authenticated user confirms deletion of a `categoria` row with `id != 1`
- **THEN** the system removes that row from the `categoria` table and redirects to the categoria list without it
#### Scenario: Deleting a categoria referenced by fiscal documents
- **WHEN** an authenticated user deletes a `categoria` row that is currently referenced by one or more `fiscal_documents.categoria_id`
- **THEN** the system completes the deletion and reassigns those fiscal documents' `categoria_id` to `1` ("Não Encontrado"), without errors or orphaned references
### Requirement: Reserved Categoria Cannot Be Deleted
The system SHALL prevent deletion of the `categoria` row with `id = 1` ("Não Encontrado"), since it is the required fallback target for automatic categorization.
#### Scenario: User attempts to delete the reserved categoria
- **WHEN** an authenticated user attempts to delete the `categoria` row with `id = 1`
- **THEN** the system rejects the deletion, shows an error message, and the row remains unchanged