Issue #220: Make document_id Optional in Document Item Creation Schema¶
Summary¶
Issue #220 addressed a validation bug where the API required document_id for creating document items, even though invoice numbers are now generated when the document is created. This caused validation failures when creating new invoices with items.
Status: ✅ COMPLETE - Schema updated, regression tests added, all tests passing.
Issues Identified¶
Required document_id During Item Creation¶
Problem: BaseDocumentItemSchema required document_id for all item creation operations
- Location: schemas/src/DocumentItemSchema.ts:13
- Impact: New invoices with items could not be created because items didn't have a document_id yet
- Affected Schemas:
- CreateInvoiceItemSchema
- CreateOfferItemSchema
- CreateCreditNoteItemSchema
Original Code:
// BaseDocumentItemSchema - Line 13
document_id: z.string({ error: "document_id_required" }).uuid({ error: "invalid_document_id" }),
Root Cause Analysis¶
Workflow Change Impact¶
When the system was updated to generate invoice numbers at document creation time (instead of draft time), the validation schemas were not updated to reflect this change.
Previous Workflow: 1. Create draft invoice → Invoice number generated → document_id available 2. Add items with document_id
Current Workflow: 1. Create invoice with items (no document_id yet) 2. Document created → Invoice number generated → document_id assigned to items
Validation Mismatch¶
The CreateInvoiceItemSchema inherited the required document_id field from BaseDocumentItemSchema, causing validation to fail when items were created without a document_id.
Solution Implementation¶
Schema Changes¶
1. Updated Create Item Schemas¶
Made document_id optional and nullable in all Create schemas:
File: schemas/src/DocumentItemSchema.ts (lines 79-100)
// Helper schemas for creation/updates
// document_id is optional during creation since invoice numbers are generated when document is created
export const CreateInvoiceItemSchema = InvoiceItemSchema.omit({
id: true,
created_at: true,
}).extend({
document_id: z.string({ error: "invalid_document_id" }).uuid({ error: "invalid_document_id" }).optional().nullable(),
});
export const CreateOfferItemSchema = OfferItemSchema.omit({
id: true,
created_at: true,
}).extend({
document_id: z.string({ error: "invalid_document_id" }).uuid({ error: "invalid_document_id" }).optional().nullable(),
});
export const CreateCreditNoteItemSchema = CreditNoteItemSchema.omit({
id: true,
created_at: true,
}).extend({
document_id: z.string({ error: "invalid_document_id" }).uuid({ error: "invalid_document_id" }).optional().nullable(),
});
Key Changes:
- Used .extend() to override document_id validation
- Changed from required to .optional().nullable()
- Removed the "document_id_required" error message (not applicable for creation)
- Added clarifying comment explaining why document_id is optional
Regression Tests¶
Added 4 Regression Tests¶
File: schemas/src/DocumentItemSchema.test.ts (lines 427-498)
Test Coverage:
1. CreateInvoiceItemSchema without document_id - Validates items can be created without document_id
2. CreateOfferItemSchema without document_id - Same for offer items
3. CreateCreditNoteItemSchema without document_id - Same for credit note items
4. Null document_id handling - Validates that null is accepted and properly handled
Example Test:
it("should validate CreateInvoiceItemSchema without document_id", () => {
const createData = {
type: ITEM_TYPE_INVOICE,
name: "Test Item",
quantity: 1,
price_net: 100,
product_category: "digital_service",
tax_rate: 19,
};
const result = CreateInvoiceItemSchema.safeParse(createData);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.document_id).toBeUndefined();
}
});
Verification¶
Test Results¶
pnpm test DocumentItemSchema
Output:
✓ src/DocumentItemSchema.test.ts (57 tests) 9ms
Test Files 1 passed (1)
Tests 57 passed (57)
All tests passed, including the 4 new regression tests.
Build Verification¶
pnpm build
Output:
ESM Build success in 31ms
CJS Build success in 30ms
DTS Build success in 1789ms
TypeScript compilation successful - no type errors.
Impact Analysis¶
Schemas Affected¶
- ✅
CreateInvoiceItemSchema- Can now accept items without document_id - ✅
CreateOfferItemSchema- Can now accept items without document_id - ✅
CreateCreditNoteItemSchema- Can now accept items without document_id - ℹ️
BaseDocumentItemSchema- Unchanged (document_id still required for complete items) - ℹ️
UpdateInvoiceItemSchema- Unchanged (document_id still required for updates)
API Impact¶
The API can now accept document creation payloads where items don't have a document_id, allowing the correct workflow:
1. Client sends document with items (no document_id in items)
2. API creates document (generates document_id)
3. API assigns document_id to all items
4. API saves items with proper document_id
Backward Compatibility¶
✅ Fully backward compatible - Existing code that provides document_id during item creation will continue to work as before. The change only makes the field optional, not forbidden.
Files Modified¶
- schemas/src/DocumentItemSchema.ts
- Updated
CreateInvoiceItemSchema(lines 81-86) - Updated
CreateOfferItemSchema(lines 88-93) -
Updated
CreateCreditNoteItemSchema(lines 95-100) -
schemas/src/DocumentItemSchema.test.ts
- Added 4 regression tests (lines 427-498)
Related Issues¶
- Issue #219: Dependency warnings (Sentry & magicast) - Resolved in parallel
- Related to the invoice number generation workflow change
Lessons Learned¶
- Schema Synchronization: When changing business logic workflows, always review and update validation schemas accordingly
- Regression Testing: Adding regression tests for bug fixes prevents re-introduction of the same issue
- Documentation: Clear comments in code explain why certain validations are optional, helping future developers understand the intent
- Gradual Migration: Optional fields provide flexibility for transitional states in document creation workflows
Future Considerations¶
- Consider adding validation at the API level to ensure document_id is properly assigned before items are persisted
- Add integration tests that verify the full document creation workflow including item creation
- Document the complete invoice creation flow in API documentation