diff --git a/CHANGELOG.md b/CHANGELOG.md index af2bdf19..abf39db9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [3.7.0] - 2026-04-06 + +### Added + +- **Close-short workflow** — accept partial fulfillment when full quantity cannot be produced or shipped; close-short preview shows per-line achievable quantities before executing (#495, #501) +- **PO accept-short** — complete a production order with less than the ordered quantity; BOM-aware guard prevents breaking assembly dependencies (#499) +- **SO line editing** — edit quantities on confirmed/in-production/on-hold/pending orders with reason tracking (#495) +- **SO line removal** — remove a line from an editable order; guarded by shipped quantity, active PO check, and minimum one-line requirement (#505, #506) +- **PO refresh-routing** — re-snapshot a product's current active routing onto an existing production order; solves POs created before routing existed (#505) +- **Quote PDF redesign** — professional B2B layout with brand colors, two-column header, itemized lines, and terms (#497) +- **Invoice PDF redesign** — professional layout with full customer info, payment terms, calculated due date, and packing slip match (#504) +- **Packing slip redesign** — matches invoice/quote style with brand header, dark table header, and alternating row stripes (#504) +- **Admin messaging** — admin-initiated direct messaging (PRO-gated; professional/enterprise tier only) (#493) + +### Fixed + +- Pending orders now included in editable statuses — line edits and removal were hidden on `pending` orders (#506) +- Quote-converted orders used `source='portal'` incorrectly — now `source='quote'`; PRO portal passes `source='portal'` explicitly (#505) +- `sales_orders.unit_price` was NOT NULL — caused conversion failures for multi-line quotes where header price is null by design (migration 077, #505) +- Packing slip header collision — "PACKING SLIP" title overlapped SO number; fixed with adequate `spaceAfter` spacing (#505) +- Close-short UI clarity — Short Closed state and multi-line Order Summary display (#502) + +### Documentation + +- Regenerated API-REFERENCE.md (438 endpoints), SCHEMA-REFERENCE.md (64 models), MIGRATIONS-LOG.md (60 migrations) +- Added close-short, line editing, and line removal workflows to orders user guide +- Added accept-short and refresh-routing sections to production user guide +- Added production shortfall path to quote-to-cash workflow +- Updated FEATURE-CATALOG.md: 41 → 50 features +- Removed stale planning document (496-architecture-review.md) + ## [3.6.0] - 2026-03-30 ### Added @@ -277,7 +308,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - AI Invoice Parser (PRO) - License management (PRO) -[Unreleased]: https://github.com/Blb3D/filaops/compare/v3.4.0...HEAD +[Unreleased]: https://github.com/Blb3D/filaops/compare/v3.7.0...HEAD +[3.7.0]: https://github.com/Blb3D/filaops/compare/v3.6.0...v3.7.0 +[3.6.0]: https://github.com/Blb3D/filaops/compare/v3.5.0...v3.6.0 +[3.5.0]: https://github.com/Blb3D/filaops/compare/v3.4.0...v3.5.0 [3.4.0]: https://github.com/Blb3D/filaops/compare/v3.3.0...v3.4.0 [3.3.0]: https://github.com/Blb3D/filaops/compare/v3.2.0...v3.3.0 [3.2.0]: https://github.com/Blb3D/filaops/compare/v3.1.1...v3.2.0 diff --git a/backend/VERSION b/backend/VERSION index 40c341bd..7c69a55d 100644 --- a/backend/VERSION +++ b/backend/VERSION @@ -1 +1 @@ -3.6.0 +3.7.0 diff --git a/docs/API-REFERENCE.md b/docs/API-REFERENCE.md index dac6af9c..232c49d5 100644 --- a/docs/API-REFERENCE.md +++ b/docs/API-REFERENCE.md @@ -10,17 +10,17 @@ | Metric | Count | | ------ | ----- | -| **Total Endpoints** | ~432 | +| **Total Endpoints** | ~438 | | **Router Files** | 49 | | **Router Groups** | 28 (including 18 admin sub-modules) | | **Base Path** | `/api/v1/` | ### HTTP Method Distribution -- **GET**: ~212 endpoints (read/query operations) -- **POST**: ~149 endpoints (create/execute operations) -- **PUT/PATCH**: ~41 endpoints (update operations) -- **DELETE**: ~30 endpoints (delete operations) +- **GET**: ~213 endpoints (read/query operations) +- **POST**: ~152 endpoints (create/execute operations) +- **PUT/PATCH**: ~42 endpoints (update operations) +- **DELETE**: ~31 endpoints (delete operations) --- @@ -80,7 +80,7 @@ Authorization: Bearer **Tier**: Core **File**: `endpoints/sales_orders.py` -**Endpoints**: 26 +**Endpoints**: 30 | Method | Path | Description | Auth | | ------ | ---- | ----------- | ---- | @@ -101,6 +101,10 @@ Authorization: Bearer | PATCH | `/sales-orders/{order_id}/shipping` | Update shipping information for an order (admin only). | ADMIN | | PATCH | `/sales-orders/{order_id}/address` | Update shipping address for an order (admin only). | ADMIN | | POST | `/sales-orders/{order_id}/cancel` | Cancel a sales order. | ADMIN | +| PATCH | `/sales-orders/{order_id}/lines` | Edit line item quantities on a sales order. | ADMIN | +| DELETE | `/sales-orders/{order_id}/lines/{line_id}` | Remove a line item from a sales order. | ADMIN | +| GET | `/sales-orders/{order_id}/close-short-preview` | Preview close-short: shows per-line achievable quantities and PO status. | ADMIN | +| POST | `/sales-orders/{order_id}/close-short` | Close an order short — accept partial fulfillment, transition to ready_to_ship. | ADMIN | | POST | `/sales-orders/{order_id}/confirm` | Confirm a pending_confirmation order from an external source. | ADMIN | | POST | `/sales-orders/{order_id}/reject` | Reject a pending_confirmation order from an external source. | ADMIN | | DELETE | `/sales-orders/{order_id}` | Delete a sales order (admin only). | ADMIN | @@ -197,7 +201,7 @@ Authorization: Bearer **Tier**: Core **File**: `endpoints/production_orders.py`, `endpoints/operation_status.py` -**Endpoints**: 40 +**Endpoints**: 42 | Method | Path | Description | Auth | | ------ | ---- | ----------- | ---- | @@ -217,7 +221,9 @@ Authorization: Bearer | POST | `/production-orders/{order_id}/release` | Release a production order for manufacturing. | CUSTOMER | | POST | `/production-orders/{order_id}/start` | Start production on an order. | CUSTOMER | | POST | `/production-orders/{order_id}/complete` | Complete a production order. | CUSTOMER | +| POST | `/production-orders/{order_id}/accept-short` | Accept a production order short — complete it with the quantity already produced. | CUSTOMER | | POST | `/production-orders/{order_id}/cancel` | Cancel a production order. | CUSTOMER | +| POST | `/production-orders/{order_id}/refresh-routing` | Re-snapshot the product's current active routing onto the production order. | CUSTOMER | | POST | `/production-orders/{order_id}/hold` | Put a production order on hold. | CUSTOMER | | PUT | `/production-orders/{order_id}/schedule` | Schedule a production order. | CUSTOMER | | GET | `/production-orders/schedule/summary` | Get production schedule summary. | CUSTOMER | @@ -971,5 +977,5 @@ All endpoints are prefixed with `/api/v1/` --- -*Last updated: 2026-03-30* -*Generated for FilaOps Core v3.6.0* +*Last updated: 2026-04-06* +*Generated for FilaOps Core v3.7.0* diff --git a/docs/FEATURE-CATALOG.md b/docs/FEATURE-CATALOG.md index 2cbe779d..fc7a86bc 100644 --- a/docs/FEATURE-CATALOG.md +++ b/docs/FEATURE-CATALOG.md @@ -9,8 +9,8 @@ | Tier | Features | Status | | ------ | ---------- | -------- | -| **Core (Open Source)** | 41 | Released | -| **Total** | 41 | - | +| **Core (Open Source)** | 50 | Released | +| **Total** | 50 | - | --- @@ -20,7 +20,7 @@ ### Core (Open Source) -## Core Features (41) +## Core Features (50) ### Authentication & Access @@ -98,6 +98,30 @@ | 40 | Duplicate Item | Clone items with inline BOM component swap for color variants | ✅ Complete | | 41 | Copy BOM | Copy a BOM to a different product with target product picker | ✅ Complete | +### Sales Orders (v3.7.0) + +| # | Feature | Description | Status | +| --- | ------- | ----------- | ------ | +| 42 | SO Line Editing | Edit quantities on confirmed/in-production/pending order lines with reason tracking | ✅ Complete | +| 43 | SO Line Removal | Remove a line from an editable order (guards: not shipped, no active POs, not last line) | ✅ Complete | +| 44 | Close-Short | Accept partial fulfillment — close an order short when full quantity cannot be produced/shipped | ✅ Complete | +| 45 | Close-Short Preview | Preview achievable quantities per line before executing close-short | ✅ Complete | +| 46 | Pending Orders Editable | Orders in 'pending' status allow line edits and removal | ✅ Complete | + +### Production Orders (v3.7.0) + +| # | Feature | Description | Status | +| --- | ------- | ----------- | ------ | +| 47 | PO Accept-Short | Complete a production order with less than the ordered quantity | ✅ Complete | +| 48 | PO Refresh Routing | Re-snapshot a product's current active routing onto an existing PO (useful when routing added after PO created) | ✅ Complete | + +### PDF Documents (v3.7.0) + +| # | Feature | Description | Status | +| --- | ------- | ----------- | ------ | +| 49 | Quote PDF Redesign | Professional B2B quote layout with brand colors, itemized lines, and terms | ✅ Complete | +| 50 | Invoice PDF Redesign | Professional invoice layout with full customer info, payment terms, and due date | ✅ Complete | + --- ## Hidden Features (Backend Only) @@ -206,5 +230,5 @@ MRP --- -*Last updated: 2026-01-28* -*Generated for FilaOps Core (Open Source)* +*Last updated: 2026-04-06* +*Generated for FilaOps Core v3.7.0* diff --git a/docs/MIGRATIONS-LOG.md b/docs/MIGRATIONS-LOG.md index 6faa558e..835a1f11 100644 --- a/docs/MIGRATIONS-LOG.md +++ b/docs/MIGRATIONS-LOG.md @@ -11,7 +11,7 @@ | Metric | Count | | ------ | ----- | -| **Total Migrations** | 56 | +| **Total Migrations** | 60 | | **Database** | PostgreSQL | | **Tool** | Alembic | @@ -29,12 +29,12 @@ | Purchasing | 5 | 019, 027, 028, 036, 2940c6a93ea7 | | Settings | 3 | 020, 037, 062 | | Performance | 1 | 905ef924f499 | -| Sales | 8 | 024, 038, 043, 061, 066, 069, 071, 072 | +| Sales | 11 | 024, 038, 043, 061, 066, 069, 071, 072, 074, 076, 077 | | Maintenance | 2 | 025, 026 | | Products | 3 | 040, 055, 065 | | Accounting | 5 | 044, 045, 046, 052, 053 | | Tax | 1 | 063 | -| Other | 2 | 070, 073 | +| Other | 3 | 070, 073, 075 | --- @@ -1121,6 +1121,65 @@ --- +#### `074_add_close_short_and_line_edit_fields.py` + +**Tier**: Core +**Date**: 2026-04-02 +**Purpose**: Add close_short fields to sales_orders and original_quantity to sales_order_lines +**Revises**: 073 + +**Adds Columns**: + +- `sales_orders.closed_short` +- `sales_orders.closed_short_at` +- `sales_orders.close_short_reason` +- `sales_order_lines.original_quantity` + +--- + +#### `075_add_close_short_records_table.py` + +**Tier**: Core +**Date**: 2026-04-03 +**Purpose**: Add close_short_records audit table +**Revises**: 074 + +**Creates Tables**: + +- `close_short_records` - Close Short Records + +**Creates Indexes**: + +- `ix_close_short_records_entity_type_entity_id` + +--- + +#### `076_add_fulfillment_status_to_so_lines.py` + +**Tier**: Core +**Date**: 2026-04-03 +**Purpose**: Add fulfillment_status to sales_order_lines +**Revises**: 075 + +**Adds Columns**: + +- `sales_order_lines.fulfillment_status` + +--- + +#### `077_make_so_unit_price_nullable.py` + +**Tier**: Core +**Date**: 2026-04-05 +**Purpose**: Make sales_orders.unit_price nullable +**Revises**: 076 + +**Alters Columns**: + +- `sales_orders.unit_price` + +--- + ## Migration Dependencies ```text @@ -1237,6 +1296,14 @@ b1815de543ea (001_initial_postgres_schema) 072_portal_ingestion_notifications | 073_add_quote_lines_table + | +074_add_close_short_and_line_edit_fields + | +075_add_close_short_records_table + | +076_add_fulfillment_status_to_so_lines + | +077_make_so_unit_price_nullable ``` @@ -1261,5 +1328,5 @@ alembic history --verbose --- -*Last updated: 2026-03-30* -*Generated for FilaOps Core v3.6.0* +*Last updated: 2026-04-06* +*Generated for FilaOps Core v3.7.0* diff --git a/docs/SCHEMA-REFERENCE.md b/docs/SCHEMA-REFERENCE.md index faa50ac5..f12b5aac 100644 --- a/docs/SCHEMA-REFERENCE.md +++ b/docs/SCHEMA-REFERENCE.md @@ -2,9 +2,9 @@ # FilaOps Database Schema Reference -**Generated:** 2026-03-30 -**Source:** FilaOps Core v3.6.0 -**Total Models:** 63 (Core only) +**Generated:** 2026-04-06 +**Source:** FilaOps Core v3.7.0 +**Total Models:** 64 (Core only) **Purpose:** AI knowledge source for codebase understanding > This is the **Core (Open Source)** schema reference. @@ -25,7 +25,7 @@ 10. [UOM Models](#uom-models) (1 model) 11. [Accounting Models](#accounting-models) (4 models) 12. [Tax Models](#tax-models) (1 model) -13. [Reference Data Models](#reference-data-models) (8 models) +13. [Reference Data Models](#reference-data-models) (9 models) --- @@ -362,7 +362,7 @@ | material_type | String(50) | NOT NULL | Material type | | color | String(50) | | Color | | finish | String(50) | NOT NULL, DEFAULT 'standard' | Finish | -| unit_price | Numeric(10, 2) | NOT NULL | Unit price | +| unit_price | Numeric(10, 2) | | Unit price | | total_price | Numeric(10, 2) | NOT NULL | Total price | | tax_amount | Numeric(10, 2) | DEFAULT 0.0 | Tax amount | | tax_rate | Numeric(5, 4) | | Tax rate | @@ -398,6 +398,9 @@ | production_notes | Text | | Production notes | | cancelled_at | DateTime | | Cancelled timestamp | | cancellation_reason | Text | | Cancellation reason | +| closed_short | Boolean | NOT NULL, DEFAULT False | Closed short | +| closed_short_at | DateTime | | Closed Short timestamp | +| close_short_reason | Text | | Close short reason | | created_at | DateTime | NOT NULL, DEFAULT utcnow, INDEX | Creation timestamp | | updated_at | DateTime | NOT NULL, DEFAULT utcnow | Last update timestamp | | confirmed_at | DateTime | | Confirmed timestamp | @@ -419,7 +422,7 @@ ### SalesOrderLine -**Table:** `sales_order_lines` | **Tier:** Core | **File:** `sales_order.py:175` +**Table:** `sales_order_lines` | **Tier:** Core | **File:** `sales_order.py:180` | Column | Type | Constraints | Description | | ------ | ---- | ----------- | ----------- | @@ -435,6 +438,8 @@ | total | Numeric(10, 2) | NOT NULL | Total | | allocated_quantity | Numeric(10, 2) | DEFAULT 0 | Quantity allocated to orders | | shipped_quantity | Numeric(10, 2) | DEFAULT 0 | Shipped quantity | +| original_quantity | Numeric(10, 2) | | Original quantity | +| fulfillment_status | String(20) | | Fulfillment status | | notes | Text | | Additional notes | | created_by | Integer | | Creator reference | @@ -1877,6 +1882,24 @@ --- +### CloseShortRecord + +**Table:** `close_short_records` | **Tier:** Core | **File:** `close_short_record.py:13` + +| Column | Type | Constraints | Description | +| ------ | ---- | ----------- | ----------- | +| id | Integer | PK, INDEX | Primary key | +| entity_type | String(20) | NOT NULL, INDEX | Entity type | +| entity_id | Integer | NOT NULL, INDEX | Entity reference | +| performed_by | Integer | FK->users.id | FK reference to users.id | +| performed_at | DateTime | DEFAULT now(), NOT NULL | Performed timestamp | +| reason | Text | | Reason | +| line_adjustments | JSON | | Line adjustments | +| linked_po_states | JSON | | Linked po states | +| inventory_snapshot | JSON | | Inventory snapshot | + +--- + ### Invoice **Table:** `invoices` | **Tier:** Core | **File:** `invoice.py:10` @@ -2037,5 +2060,5 @@ | UOM Models | 1 | 1 | | Accounting Models | 4 | 4 | | Tax Models | 1 | 1 | -| Reference Data Models | 8 | 8 | -| **Total** | **63** | **63** | +| Reference Data Models | 9 | 9 | +| **Total** | **64** | **64** | diff --git a/docs/user-guide/orders.md b/docs/user-guide/orders.md index a9cd7812..20d557fd 100644 --- a/docs/user-guide/orders.md +++ b/docs/user-guide/orders.md @@ -448,6 +448,53 @@ For a detailed walkthrough of this end-to-end process, see the [Quote to Cash](w --- +## Editing Order Lines + +Admins can edit line quantities and remove lines on orders in **pending**, **confirmed**, **in_production**, or **on_hold** status. + +### Changing a Quantity + +1. Open the order detail view +2. In the **Line Items** table, click **Edit** on the line +3. Enter the new quantity (cannot go below already-shipped amount) +4. Enter a reason — this is recorded in the order history +5. Click ✓ to save + +### Removing a Line + +A **✕** button appears next to each line when: + +- The order has more than one line +- The line has not been shipped +- No non-cancelled production orders are linked to that line (including completed or closed POs) + +Click **✕** → confirm the prompt → the line is removed and totals recalculate automatically. + +> **Note**: If any non-cancelled production order (including completed or closed states) exists for the line, cancel it first before removing the line. + +--- + +## Close-Short Workflow + +Close-short lets you accept partial fulfillment when the full ordered quantity cannot be produced or shipped. This closes the order without waiting indefinitely for the remaining units. + +### When to Use + +- A production order completed with fewer units than ordered (scrap, material shortage) +- Customer agreed to accept partial shipment +- You want to close out an order rather than leave it open + +### How It Works + +1. Open the order detail — you'll see a **Close Short** button when the order is in `confirmed`, `in_production`, or `ready_to_ship` status +2. Click **Close Short** → a preview modal shows per-line achievable quantities based on completed production +3. Review the breakdown — lines that can't be fully fulfilled are flagged +4. Confirm → FilaOps adjusts line quantities to what was actually fulfilled, marks the order closed-short, and updates fulfillment status + +> **Tip**: Use **PO Accept-Short** on linked production orders first to lock in completed quantities, then use Close-Short on the sales order. + +--- + ## Tips & Best Practices - **Use quotes for custom work** — Quotes give customers time to review pricing and let you track conversion rates @@ -479,6 +526,9 @@ With orders flowing, you'll want to manage the production and delivery side: | View order details | **Sales > Orders** > Click an order card | | Generate a production order | Order detail view > **Generate Production Order** | | Cancel an order | Order detail view > **Cancel Order** | +| Edit a line quantity | Order detail view > Line Items table > **Edit** | +| Remove a line | Order detail view > Line Items table > **✕** | +| Close an order short | Order detail view > **Close Short** | | Add a tracking number | **Sales > Shipping** > **Needs Label** tab > Enter tracking | | Mark an order as shipped | **Sales > Shipping** > **Ready to Ship** tab > **Mark Shipped** | | Print a packing slip | **Sales > Shipping** > **Packing Slip** button | diff --git a/docs/user-guide/production.md b/docs/user-guide/production.md index a76b28fb..4e0a7751 100644 --- a/docs/user-guide/production.md +++ b/docs/user-guide/production.md @@ -333,6 +333,36 @@ Each order card in the queue shows: --- +--- + +## Accepting Short (Partial Completion) + +When a production order cannot be completed at full quantity — due to scrap, material shortage, or equipment failure — use **Accept Short** to close it with what was actually produced. + +1. Open the production order detail +2. Click **Accept Short** (available when the order has some completed quantity but cannot reach the ordered amount) +3. Confirm — FilaOps records the completed quantity, marks the PO closed-short, and updates inventory accordingly + +> **Important**: Accept Short respects BOM assembly dependencies. If the PO is a sub-component for an assembly, FilaOps will warn you if accepting short breaks the parent assembly's material requirements. + +After accepting short on all linked POs, use [Close-Short](orders.md#close-short-workflow) on the sales order to accept partial fulfillment at the order level. + +--- + +## Refreshing Routing + +If a routing was added or updated *after* a production order was already created, the PO won't automatically pick up the new operations. Use **Refresh Routing** to re-apply the current active routing. + +1. Open the production order modal +2. Click **Refresh Routing** in the footer (visible for `draft`, `released`, and `on_hold` orders) +3. Confirm — all pending operations are replaced with the current routing's operations and materials + +If the order has no operations at all (routing was missing when the PO was created), an **Apply Routing Now** button appears directly in the operations section. + +> **Note**: Refresh Routing is blocked if any operation has already been started or completed — you can't rewrite history for work already in progress. + +--- + ## Tips & Best Practices - **Set up work centers before creating production orders** — this lets you assign work to specific machines and track capacity @@ -364,6 +394,8 @@ With production running, you'll need to manage the materials and inventory side: | Generate from a sales order | **Sales > Orders** > Order detail > **Generate Production Order** | | Release an order to the floor | Production order detail > **Release** | | Complete a production order | Production order detail > **Complete** | +| Accept short (partial completion) | Production order detail > **Accept Short** | +| Refresh routing on a PO | Production order modal > **Refresh Routing** | | Split a production order | Production order detail > **Split Order** | | Scrap a production order | Production order detail > **Scrap** | | Run a QC inspection | Production order detail > **QC Inspection** | diff --git a/docs/user-guide/workflows/quote-to-cash.md b/docs/user-guide/workflows/quote-to-cash.md index 540408ff..b6c20afd 100644 --- a/docs/user-guide/workflows/quote-to-cash.md +++ b/docs/user-guide/workflows/quote-to-cash.md @@ -102,6 +102,18 @@ Once items are produced and in stock, ship the sales order. --- +## Step 5b: Handle a Production Shortfall (Optional) + +If production cannot deliver the full ordered quantity: + +1. On the production order, click **Accept Short** to complete it with the quantity actually produced +2. On the sales order, click **Close Short** — a preview shows per-line achievable quantities +3. Confirm close-short — FilaOps adjusts quantities, closes the order, and records the shortfall + +Skip this step if production completed in full. + +--- + ## Step 6: Record Payment When the customer pays, record the payment against the order. @@ -136,7 +148,8 @@ Confirm the transaction flowed through correctly. - [ ] Quote created and sent to customer - [ ] Quote accepted and converted to sales order - [ ] Production orders created for manufactured items -- [ ] Production completed and goods in stock +- [ ] Production completed (or accepted short if partial) +- [ ] Sales order closed-short if partial fulfillment (optional) - [ ] Sales order shipped - [ ] Payment recorded - [ ] Revenue verified in accounting