Tax Filings — GraphQL Relations¶
Date: 2026-06-15
Problem / motivation¶
The existing TaxRate, TaxPeriod, and RetentionCertificate types exposed FK id fields (accountPayableId, declarationFileId, pdfFileId, etc.) but not the resolved objects behind them. The frontend had to perform separate queries to fetch the related account, file, user, or ledger-entry records, and there was no way to eager-load them in a single GraphQL request.
In scope¶
- Expose resolved relation fields on
TaxRate,TaxPeriod, andRetentionCertificate(detailed below). - Create per-entity dataloaders where none existed:
FileLoader,UserLoader,LedgerEntryLoader,TaxPeriodLoader.
Out of scope¶
- No DB schema changes — FK columns already exist.
- No new queries or mutations.
- No changes to
TaxRate/TaxPeriod/RetentionCertificatecreation or update logic.
Data model changes¶
None — this is a pure GraphQL layer change on top of existing FK columns.
GraphQL surface¶
TaxRate — new fields¶
type TaxRate {
# existing …
accountPayable: Account # resolves accountPayableId
accountReceivable: Account # resolves accountReceivableId
withholding: Account # resolves withholdingAccountId
}
TaxPeriod — new fields¶
type TaxPeriod {
# existing …
declarationFile: FileLite # resolves declarationFileId
paymentLedgerEntry: LedgerEntry # resolves paymentLedgerEntryId
filedBy: User # resolves filedById
}
RetentionCertificate — new fields¶
type RetentionCertificate {
# existing …
pdfFile: FileLite # resolves pdfFileId
period: TaxPeriod! # resolves periodId
}
What was implemented¶
- New dataloaders
app/graphql/files/loaders/file_loader.pyapp/graphql/users/loaders/user_loader.pyapp/graphql/ledger/loaders/ledger_entry_loader.pyapp/graphql/tax_filings/loaders/tax_period_loader.py- Updated response types in
app/graphql/tax_filings/strawberry/tax_rate_response.pyandapp/graphql/tax_filings/strawberry/tax_filing_response.py— added@strawberry.fieldasync resolvers for every relation. - Tests in
tests/graphql/tax_filings/test_relations.pycovering None-guards and successful load paths.
RBAC¶
No change — all new fields live on existing types behind the existing TAX_FILINGS path.
Frontend contract¶
All new fields are optional (nullable) except RetentionCertificate.period which is non-null.
- To display the GL accounts on a tax rate: select
accountPayable { id accountCode accountNumber }, etc. - To display the declaration file on a period: select
declarationFile { id name url }. - To display the payment journal: select
paymentLedgerEntry { id entryNumber entryDate memo }. - To display who filed the period: select
filedBy { id firstName lastName email }. - On a certificate detail page: select
period { id periodType startDate endDate status }andpdfFile { id name url }.
Future additions¶
RetentionCertificate.entity— the entity relation (client or supplier) is polymorphic (based onentityKind); deferred because it requires a union type or separate resolver per kind.