@happyvertical/smrt-expenses 0.60.13 → 0.60.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/AGENTS.md +15 -2
  2. package/README.md +8 -0
  3. package/agents/allocation-test-matrix.md +82 -0
  4. package/agents/reviewed-allocations.md +138 -0
  5. package/dist/allocation-internal.d.ts +6 -0
  6. package/dist/allocation-internal.d.ts.map +1 -0
  7. package/dist/chunks/{Expense-CimA4lxE.js → Expense-Deq19w2r.js} +28 -3
  8. package/dist/chunks/Expense-Deq19w2r.js.map +1 -0
  9. package/dist/chunks/{ExpenseReceiptCollection-CSBrNDzv.js → ExpenseReceiptCollection-D4Na93BF.js} +37 -3
  10. package/dist/chunks/ExpenseReceiptCollection-D4Na93BF.js.map +1 -0
  11. package/dist/chunks/{expense-actions-DiIhw0xO.js → expense-actions-DszuHacx.js} +2 -2
  12. package/dist/chunks/{expense-actions-DiIhw0xO.js.map → expense-actions-DszuHacx.js.map} +1 -1
  13. package/dist/collections/ExpenseCollection.d.ts +5 -5
  14. package/dist/collections/ExpenseCollection.d.ts.map +1 -1
  15. package/dist/expense-allocation.d.ts +133 -0
  16. package/dist/expense-allocation.d.ts.map +1 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1001 -21
  20. package/dist/index.js.map +1 -1
  21. package/dist/manifest.json +2359 -627
  22. package/dist/models/Expense.d.ts +2 -6
  23. package/dist/models/Expense.d.ts.map +1 -1
  24. package/dist/models/ExpenseAllocation.d.ts +117 -0
  25. package/dist/models/ExpenseAllocation.d.ts.map +1 -0
  26. package/dist/models/ExpenseReceipt.d.ts +7 -0
  27. package/dist/models/ExpenseReceipt.d.ts.map +1 -1
  28. package/dist/permissions.d.ts +5 -0
  29. package/dist/permissions.d.ts.map +1 -0
  30. package/dist/recipes.d.ts +3 -0
  31. package/dist/recipes.d.ts.map +1 -1
  32. package/dist/smrt-knowledge.json +746 -108
  33. package/dist/smrt-knowledge.okf.md +17 -3
  34. package/dist/types.d.ts +2 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/types.js.map +1 -1
  37. package/package.json +9 -9
  38. package/dist/chunks/Expense-CimA4lxE.js.map +0 -1
  39. package/dist/chunks/ExpenseReceiptCollection-CSBrNDzv.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,11 +1,978 @@
1
- import { n as ExpenseReceipt, r as __exportAll, t as ExpenseReceiptCollection } from "./chunks/ExpenseReceiptCollection-CSBrNDzv.js";
2
- import { t as ExpenseActionError } from "./chunks/principal-_1CdcvvW.js";
1
+ import { n as ExpenseReceipt, r as __exportAll, t as ExpenseReceiptCollection } from "./chunks/ExpenseReceiptCollection-D4Na93BF.js";
2
+ import { i as withActionPrincipal, n as authorizeExpenseAction, t as ExpenseActionError } from "./chunks/principal-_1CdcvvW.js";
3
+ import { d as allocationHeads, f as allocationWrites, i as computeContentSha256, p as insertAllocation, r as assertQualifiedClassName, s as instantMs, t as Expense } from "./chunks/Expense-Deq19w2r.js";
3
4
  import { DuplicateReceiptError, EXPENSE_PAYERS, EXPENSE_REVIEW_STATUSES, ExpenseError, SUGGESTED_EXPENSE_CATEGORIES } from "./types.js";
4
- import { r as computeContentSha256, t as Expense } from "./chunks/Expense-CimA4lxE.js";
5
- import { ObjectRegistry, SmrtCollection, SmrtRecipe, method, smrt } from "@happyvertical/smrt-core";
6
- import { ContractLineItemCollection, PurchaseContractCollection } from "@happyvertical/smrt-commerce";
5
+ import { ObjectRegistry, SmrtCollection, SmrtObject, SmrtRecipe, crossPackageRef, field, foreignKey, isPostgresDatabase, method, smrt } from "@happyvertical/smrt-core";
6
+ import { TenantScoped, tenantId } from "@happyvertical/smrt-tenancy";
7
+ import { registerPermissionDefinitions } from "@happyvertical/smrt-users";
8
+ import { createHash, randomBytes } from "node:crypto";
9
+ import { ContractLineItemCollection, InvoiceCollection, InvoiceLineItemCollection, PurchaseContractCollection, bindInvoiceLineSource, registerInvoiceSourceBinding, unbindInvoiceLineSource } from "@happyvertical/smrt-commerce";
7
10
  //#region src/__smrt-register__.ts
8
- ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-expenses","packageVersion":"0.60.13","objects":{"@happyvertical/smrt-expenses:ExpenseCollection":{"name":"expensecollection","className":"ExpenseCollection","qualifiedName":"@happyvertical/smrt-expenses:ExpenseCollection","collection":"expenses","description":"Collection for Expense. Reads go through the normal tenant interceptors, so inside withTenant() every method here sees only that tenant's expenses.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/collections/ExpenseCollection.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{"recordSupplierPayable":{"name":"recordSupplierPayable","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"RecordSupplierPayableInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"recordSupplierPayableEvent":{"name":"recordSupplierPayableEvent","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"RecordSupplierPayableEventInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"completeSupplierPayableBasis":{"name":"completeSupplierPayableBasis","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"CompleteSupplierPayableBasisInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"supplierPayablePosition":{"name":"supplierPayablePosition","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"object","optional":true,"memberTypes":["string"]}],"returnType":"Promise<SupplierPayablePosition | null>","isStatic":false,"isPublic":true},"reimbursementPosition":{"name":"reimbursementPosition","async":true,"parameters":[{"name":"sourceId","type":"string","optional":false}],"returnType":"Promise<ExpenseReimbursementPosition>","isStatic":false,"isPublic":true},"refundPosition":{"name":"refundPosition","async":true,"parameters":[{"name":"sourceId","type":"string","optional":false}],"returnType":"Promise<ExpenseRefundPosition>","isStatic":false,"isPublic":true},"record":{"name":"record","async":true,"parameters":[{"name":"options","type":"RecordExpenseActionInput","optional":false}],"returnType":"Promise<Expense>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"record","effect":"write","idempotent":false,"openWorld":false,"description":"Record a new expense for review (service-backed)."}},"attachReceipt":{"name":"attachReceipt","async":true,"parameters":[{"name":"options","type":"AttachExpenseReceiptActionInput","optional":false}],"returnType":"Promise<ExpenseReceipt>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"attachReceipt","effect":"write","idempotent":false,"openWorld":false,"description":"Attach a receipt file to an expense (service-backed)."}},"recordReview":{"name":"recordReview","async":true,"parameters":[{"name":"options","type":"ReviewExpenseActionInput","optional":false}],"returnType":"Promise<Expense>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"recordReview","effect":"write","idempotent":false,"openWorld":false,"description":"Approve or reject an expense with a reason (service-backed)."}},"recordReimbursement":{"name":"recordReimbursement","async":true,"parameters":[{"name":"options","type":"MarkExpenseReimbursedActionInput","optional":false}],"returnType":"Promise<Expense>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"recordReimbursement","effect":"write","idempotent":false,"openWorld":false,"description":"Record that an expense was reimbursed (service-backed)."}},"forCostObject":{"name":"forCostObject","async":true,"parameters":[{"name":"costObjectType","type":"string","optional":false},{"name":"costObjectId","type":"string","optional":false}],"returnType":"Promise<Expense[]>","isStatic":false,"isPublic":true},"totalsByCurrency":{"name":"totalsByCurrency","async":true,"parameters":[{"name":"options","type":"ExpenseTotalsOptions","optional":true}],"returnType":"Promise<CurrencyTotals>","isStatic":false,"isPublic":true},"listReviewedTaxFacts":{"name":"listReviewedTaxFacts","async":true,"parameters":[{"name":"options","type":"ReviewedExpenseTaxFactsOptions","optional":false}],"returnType":"Promise<ReviewedExpenseTaxFact[]>","isStatic":false,"isPublic":true},"commitmentPosition":{"name":"commitmentPosition","async":true,"parameters":[{"name":"commitmentId","type":"string","optional":false},{"name":"options","type":"object","optional":true,"memberTypes":["string"]}],"returnType":"Promise<CommitmentPosition>","isStatic":false,"isPublic":true}},"decoratorConfig":{"api":{"include":["record","attachReceipt","recordReview","recordReimbursement"]},"mcp":{"include":[]},"cli":false,"tableName":"expenses"},"extends":"SmrtCollection","extendsTypeArg":"Expense","exportName":"ExpenseCollection","collectionExportName":"ExpenseCollectionCollection","collectionOf":"@happyvertical/smrt-expenses:Expense","schema":{"tableName":"expenses","ddl":"CREATE TABLE IF NOT EXISTS \\"expenses\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"cost_object_type\\" TEXT,\\n \\"cost_object_id\\" TEXT,\\n \\"category\\" TEXT,\\n \\"description\\" TEXT,\\n \\"amount\\" INTEGER,\\n \\"currency\\" TEXT,\\n \\"financial_breakdown\\" TEXT,\\n \\"supplier_payable_json\\" TEXT,\\n \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\n \\"refund_recipient\\" TEXT,\\n \\"incurred_on\\" TEXT,\\n \\"recorded_at\\" TIMESTAMP,\\n \\"recorded_by_profile_id\\" UUID,\\n \\"paid_by\\" TEXT,\\n \\"paid_by_profile_id\\" UUID,\\n \\"vendor_id\\" UUID,\\n \\"commitment_id\\" UUID,\\n \\"commitment_line_id\\" UUID,\\n \\"review_status\\" TEXT,\\n \\"reviewed_by_profile_id\\" UUID,\\n \\"reviewed_at\\" TIMESTAMP,\\n \\"review_note\\" TEXT,\\n \\"duplicate_of_id\\" UUID,\\n \\"reimbursable\\" BOOLEAN,\\n \\"reimbursed_amount\\" INTEGER,\\n \\"reimbursed_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"category":{"type":"TEXT","notNull":false,"unique":false},"description":{"type":"TEXT","notNull":false,"unique":false},"amount":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false},"financial_breakdown":{"type":"TEXT","notNull":false,"unique":false},"supplier_payable_json":{"type":"TEXT","notNull":false,"unique":false},"paid_by_name":{"type":"TEXT","notNull":false,"unique":false},"refund_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"refund_recipient":{"type":"TEXT","notNull":false,"unique":false},"incurred_on":{"type":"TEXT","notNull":false,"unique":false},"recorded_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"recorded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"paid_by":{"type":"TEXT","notNull":false,"unique":false},"paid_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"vendor_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_line_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"review_status":{"type":"TEXT","notNull":false,"unique":false},"reviewed_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"reviewed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"review_note":{"type":"TEXT","notNull":false,"unique":false},"duplicate_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"SET NULL","onUpdate":"CASCADE"}},"reimbursable":{"type":"BOOLEAN","notNull":false,"unique":false},"reimbursed_amount":{"type":"INTEGER","notNull":false,"unique":false},"reimbursed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"reimbursement_reference":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expenses_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expenses_cost_object_idx","columns":["tenant_id","cost_object_type","cost_object_id","incurred_on"]},{"name":"expenses_commitment_review_idx","columns":["tenant_id","commitment_id","review_status"]},{"name":"expenses_review_status_idx","columns":["tenant_id","review_status","incurred_on"]},{"name":"expenses_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expenses_refund_of_id_idx","columns":["refund_of_id"]},{"name":"expenses_recorded_by_profile_id_idx","columns":["recorded_by_profile_id"]},{"name":"expenses_paid_by_profile_id_idx","columns":["paid_by_profile_id"]},{"name":"expenses_vendor_id_idx","columns":["vendor_id"]},{"name":"expenses_commitment_id_idx","columns":["commitment_id"]},{"name":"expenses_commitment_line_id_idx","columns":["commitment_line_id"]},{"name":"expenses_reviewed_by_profile_id_idx","columns":["reviewed_by_profile_id"]},{"name":"expenses_duplicate_of_id_idx","columns":["duplicate_of_id"]}],"version":"81a9a368"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseReceiptCollection":{"name":"expensereceiptcollection","className":"ExpenseReceiptCollection","qualifiedName":"@happyvertical/smrt-expenses:ExpenseReceiptCollection","collection":"expensereceipts","description":"Junction collection over expense_receipts (left: expense, right: asset). Reads go through the normal tenant interceptors.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/collections/ExpenseReceiptCollection.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{"byRight":{"name":"byRight","async":true,"parameters":[{"name":"rightId","type":"string","optional":false},{"name":"opts","type":"JunctionFilterOptions","optional":true}],"returnType":"Promise<TItem[]>","isStatic":false,"isPublic":true},"byLeft":{"name":"byLeft","async":true,"parameters":[{"name":"leftId","type":"string","optional":false},{"name":"opts","type":"JunctionFilterOptions","optional":true}],"returnType":"Promise<TItem[]>","isStatic":false,"isPublic":true},"attach":{"name":"attach","async":true,"parameters":[{"name":"_expenseId","type":"string","optional":false},{"name":"_assetId","type":"string","optional":false},{"name":"_opts","type":"JunctionAttachOptions","optional":true}],"returnType":"Promise<ExpenseReceipt>","isStatic":false,"isPublic":true},"detach":{"name":"detach","async":true,"parameters":[{"name":"leftId","type":"string","optional":false},{"name":"rightId","type":"string","optional":false},{"name":"opts","type":"JunctionFilterOptions","optional":true}],"returnType":"Promise<void>","isStatic":false,"isPublic":true},"setLinks":{"name":"setLinks","async":true,"parameters":[{"name":"_expenseId","type":"string","optional":false},{"name":"_assetIds","type":"string[]","optional":false},{"name":"_opts","type":"JunctionAttachOptions","optional":true}],"returnType":"Promise<void>","isStatic":false,"isPublic":true},"attachReceipt":{"name":"attachReceipt","async":true,"parameters":[{"name":"input","type":"AttachReceiptInput","optional":false}],"returnType":"Promise<ExpenseReceipt>","isStatic":false,"isPublic":true},"receiptsFor":{"name":"receiptsFor","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false}],"returnType":"Promise<ExpenseReceipt[]>","isStatic":false,"isPublic":true},"findDuplicateReceipts":{"name":"findDuplicateReceipts","async":true,"parameters":[{"name":"options","type":"FindDuplicateReceiptsOptions","optional":true}],"returnType":"Promise<DuplicateReceiptGroup[]>","isStatic":false,"isPublic":true}},"decoratorConfig":{"api":{"include":[]},"mcp":{"include":[]},"cli":false,"tableName":"expense_receipts"},"extends":"SmrtJunction","extendsTypeArg":"ExpenseReceipt","exportName":"ExpenseReceiptCollection","collectionExportName":"ExpenseReceiptCollectionCollection","collectionOf":"@happyvertical/smrt-expenses:ExpenseReceipt","schema":{"tableName":"expense_receipts","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_receipts\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"expense_id\\" UUID NOT NULL,\\n \\"asset_id\\" UUID NOT NULL,\\n \\"uploaded_by_profile_id\\" UUID,\\n \\"filename\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"byte_count\\" INTEGER,\\n \\"content_sha256\\" TEXT NOT NULL\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"CASCADE","onUpdate":"CASCADE"}},"asset_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"uploaded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"filename":{"type":"TEXT","notNull":false,"unique":false},"mime_type":{"type":"TEXT","notNull":false,"unique":false},"byte_count":{"type":"INTEGER","notNull":false,"unique":false},"content_sha256":{"type":"TEXT","notNull":true,"unique":false}},"indexes":[{"name":"expense_receipts_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expense_receipts_expense_sha256_key","columns":["expense_id","content_sha256"],"unique":true},{"name":"expense_receipts_tenant_sha256_idx","columns":["tenant_id","content_sha256"]},{"name":"expense_receipts_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_receipts_asset_id_idx","columns":["asset_id"]},{"name":"expense_receipts_uploaded_by_profile_id_idx","columns":["uploaded_by_profile_id"]}],"version":"8ed688ee"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:Expense":{"name":"expense","className":"Expense","qualifiedName":"@happyvertical/smrt-expenses:Expense","collection":"expenses","description":"A cost someone incurred.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/Expense.ts","packageName":"@happyvertical/smrt-expenses","fields":{"tenantId":{"type":"text","required":false,"_meta":{"sqlType":"UUID","nullable":true,"__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":false,"autoPopulate":true,"nullable":true,"mode":"optional","field":"tenantId","allowSuperAdminBypass":false}}},"costObjectType":{"type":"text","required":false,"_meta":{}},"costObjectId":{"type":"text","required":false,"_meta":{}},"category":{"type":"text","required":false,"_meta":{}},"description":{"type":"text","required":false,"_meta":{}},"amount":{"type":"integer","required":false,"_meta":{}},"currency":{"type":"text","required":false,"_meta":{}},"financialBreakdown":{"type":"text","required":false,"_meta":{}},"supplierPayableJson":{"type":"text","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"paidByName":{"type":"text","required":false,"_meta":{}},"refundOfId":{"type":"foreignKey","required":false,"related":"Expense","_meta":{"onDelete":"RESTRICT"}},"refundRecipient":{"type":"text","required":false,"_meta":{}},"incurredOn":{"type":"text","required":false,"_meta":{}},"recordedAt":{"type":"datetime","required":false,"_meta":{}},"recordedByProfileId":{"type":"crossPackageRef","required":false,"related":"PROFILE"},"paidBy":{"type":"text","required":false,"_meta":{},"enum":["company","person","third-party"]},"paidByProfileId":{"type":"crossPackageRef","required":false,"related":"PROFILE"},"vendorId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:Vendor"},"commitmentId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:PurchaseContract","_meta":{"validateSubtype":true}},"commitmentLineId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:ContractLineItem"},"reviewStatus":{"type":"text","required":false,"_meta":{"readonly":true},"enum":["unreviewed","reviewed","rejected"],"readonly":true},"reviewedByProfileId":{"type":"crossPackageRef","required":false,"related":"PROFILE"},"reviewedAt":{"type":"datetime","required":false,"_meta":{"readonly":true},"readonly":true},"reviewNote":{"type":"text","required":false,"_meta":{"readonly":true},"readonly":true},"duplicateOfId":{"type":"foreignKey","required":false,"related":"Expense","_meta":{"onDelete":"SET NULL"}},"reimbursable":{"type":"boolean","required":false,"_meta":{}},"reimbursedAmount":{"type":"integer","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"reimbursedAt":{"type":"datetime","required":false,"_meta":{"readonly":true},"readonly":true},"reimbursementReference":{"type":"text","required":false,"_meta":{"readonly":true},"readonly":true}},"methods":{"getFinancialBreakdown":{"name":"getFinancialBreakdown","async":false,"parameters":[],"returnType":"ExpenseFinancialBreakdown | null","isStatic":false,"isPublic":true},"setFinancialBreakdown":{"name":"setFinancialBreakdown","async":false,"parameters":[{"name":"value","type":"ExpenseFinancialBreakdown | null","optional":false,"unionBranches":[{"type":"ExpenseFinancialBreakdown"},{"type":"null"}]}],"returnType":"void","isStatic":false,"isPublic":true},"getRefundRecipient":{"name":"getRefundRecipient","async":false,"parameters":[],"returnType":"ExpenseRefundRecipient | null","isStatic":false,"isPublic":true},"setRefundRecipient":{"name":"setRefundRecipient","async":false,"parameters":[{"name":"value","type":"ExpenseRefundRecipient | null","optional":false,"unionBranches":[{"type":"ExpenseRefundRecipient"},{"type":"null"}]}],"returnType":"void","isStatic":false,"isPublic":true},"getCostBasis":{"name":"getCostBasis","async":false,"parameters":[],"returnType":"ExpenseCostBasis | null","isStatic":false,"isPublic":true},"isReimbursed":{"name":"isReimbursed","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"isCountable":{"name":"isCountable","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"review":{"name":"review","async":true,"parameters":[{"name":"input","type":"ReviewExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"reject":{"name":"reject","async":true,"parameters":[{"name":"input","type":"RejectExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"markDuplicate":{"name":"markDuplicate","async":true,"parameters":[{"name":"input","type":"MarkDuplicateExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"reopen":{"name":"reopen","async":true,"parameters":[{"name":"input","type":"ReopenExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"markReimbursed":{"name":"markReimbursed","async":true,"parameters":[{"name":"input","type":"MarkReimbursedInput","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"getCostObject":{"name":"getCostObject","async":true,"parameters":[],"returnType":"Promise<T | null>","isStatic":false,"isPublic":true},"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"recordSupplierPayable":{"name":"recordSupplierPayable","async":true,"parameters":[{"name":"input","type":"RecordSupplierPayableInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"recordSupplierPayableEvent":{"name":"recordSupplierPayableEvent","async":true,"parameters":[{"name":"input","type":"RecordSupplierPayableEventInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"completeSupplierPayableBasis":{"name":"completeSupplierPayableBasis","async":true,"parameters":[{"name":"input","type":"CompleteSupplierPayableBasisInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"getSupplierPayablePosition":{"name":"getSupplierPayablePosition","async":true,"parameters":[{"name":"input","type":"object","optional":true,"memberTypes":["string"]}],"returnType":"Promise<SupplierPayablePosition | null>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[{"name":"options","type":"object","optional":true,"memberTypes":["Date | string"]}],"returnType":"Promise<void>","isStatic":false,"isPublic":true},"getReimbursementPosition":{"name":"getReimbursementPosition","async":true,"parameters":[],"returnType":"Promise<ExpenseReimbursementPosition>","isStatic":false,"isPublic":true},"getRefundPosition":{"name":"getRefundPosition","async":true,"parameters":[],"returnType":"Promise<ExpenseRefundPosition>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expenses","api":{"include":["record","attachReceipt","recordReview","recordReimbursement"]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expenses_cost_object_idx","columns":["tenantId","costObjectType","costObjectId","incurredOn"]},{"name":"expenses_commitment_review_idx","columns":["tenantId","commitmentId","reviewStatus"]},{"name":"expenses_review_status_idx","columns":["tenantId","reviewStatus","incurredOn"]}],"tenantScoped":{"mode":"optional"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"Expense","collectionExportName":"ExpenseCollection","schema":{"tableName":"expenses","ddl":"CREATE TABLE IF NOT EXISTS \\"expenses\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"cost_object_type\\" TEXT,\\n \\"cost_object_id\\" TEXT,\\n \\"category\\" TEXT,\\n \\"description\\" TEXT,\\n \\"amount\\" INTEGER,\\n \\"currency\\" TEXT,\\n \\"financial_breakdown\\" TEXT,\\n \\"supplier_payable_json\\" TEXT,\\n \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\n \\"refund_recipient\\" TEXT,\\n \\"incurred_on\\" TEXT,\\n \\"recorded_at\\" TIMESTAMP,\\n \\"recorded_by_profile_id\\" UUID,\\n \\"paid_by\\" TEXT,\\n \\"paid_by_profile_id\\" UUID,\\n \\"vendor_id\\" UUID,\\n \\"commitment_id\\" UUID,\\n \\"commitment_line_id\\" UUID,\\n \\"review_status\\" TEXT,\\n \\"reviewed_by_profile_id\\" UUID,\\n \\"reviewed_at\\" TIMESTAMP,\\n \\"review_note\\" TEXT,\\n \\"duplicate_of_id\\" UUID,\\n \\"reimbursable\\" BOOLEAN,\\n \\"reimbursed_amount\\" INTEGER,\\n \\"reimbursed_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"category":{"type":"TEXT","notNull":false,"unique":false},"description":{"type":"TEXT","notNull":false,"unique":false},"amount":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false},"financial_breakdown":{"type":"TEXT","notNull":false,"unique":false},"supplier_payable_json":{"type":"TEXT","notNull":false,"unique":false},"paid_by_name":{"type":"TEXT","notNull":false,"unique":false},"refund_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"refund_recipient":{"type":"TEXT","notNull":false,"unique":false},"incurred_on":{"type":"TEXT","notNull":false,"unique":false},"recorded_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"recorded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"paid_by":{"type":"TEXT","notNull":false,"unique":false},"paid_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"vendor_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_line_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"review_status":{"type":"TEXT","notNull":false,"unique":false},"reviewed_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"reviewed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"review_note":{"type":"TEXT","notNull":false,"unique":false},"duplicate_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"SET NULL","onUpdate":"CASCADE"}},"reimbursable":{"type":"BOOLEAN","notNull":false,"unique":false},"reimbursed_amount":{"type":"INTEGER","notNull":false,"unique":false},"reimbursed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"reimbursement_reference":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expenses_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expenses_cost_object_idx","columns":["tenant_id","cost_object_type","cost_object_id","incurred_on"]},{"name":"expenses_commitment_review_idx","columns":["tenant_id","commitment_id","review_status"]},{"name":"expenses_review_status_idx","columns":["tenant_id","review_status","incurred_on"]},{"name":"expenses_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expenses_refund_of_id_idx","columns":["refund_of_id"]},{"name":"expenses_recorded_by_profile_id_idx","columns":["recorded_by_profile_id"]},{"name":"expenses_paid_by_profile_id_idx","columns":["paid_by_profile_id"]},{"name":"expenses_vendor_id_idx","columns":["vendor_id"]},{"name":"expenses_commitment_id_idx","columns":["commitment_id"]},{"name":"expenses_commitment_line_id_idx","columns":["commitment_line_id"]},{"name":"expenses_reviewed_by_profile_id_idx","columns":["reviewed_by_profile_id"]},{"name":"expenses_duplicate_of_id_idx","columns":["duplicate_of_id"]}],"version":"81a9a368"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseReceipt":{"name":"expensereceipt","className":"ExpenseReceipt","qualifiedName":"@happyvertical/smrt-expenses:ExpenseReceipt","collection":"expensereceipts","description":"A receipt file attached to an expense.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/ExpenseReceipt.ts","packageName":"@happyvertical/smrt-expenses","fields":{"tenantId":{"type":"text","required":false,"_meta":{"sqlType":"UUID","nullable":true,"__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":false,"autoPopulate":true,"nullable":true,"mode":"optional","field":"tenantId","allowSuperAdminBypass":false}}},"expenseId":{"type":"foreignKey","required":true,"related":"Expense","_meta":{"required":true,"onDelete":"CASCADE"}},"assetId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-assets:Asset"},"uploadedByProfileId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-profiles:Profile"},"filename":{"type":"text","required":false,"_meta":{}},"mimeType":{"type":"text","required":false,"_meta":{}},"byteCount":{"type":"integer","required":false,"_meta":{}},"contentSha256":{"type":"text","required":true,"_meta":{"required":true}}},"methods":{"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expense_receipts","api":{"include":[]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expense_receipts_expense_sha256_key","columns":["expenseId","contentSha256"],"unique":true},{"name":"expense_receipts_tenant_sha256_idx","columns":["tenantId","contentSha256"]}],"tenantScoped":{"mode":"optional"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ExpenseReceipt","collectionExportName":"ExpenseReceiptCollection","validationRules":[{"field":"expenseId","rule":"required","fieldType":"foreignKey"},{"field":"assetId","rule":"required","fieldType":"crossPackageRef"},{"field":"contentSha256","rule":"required","fieldType":"text"}],"schema":{"tableName":"expense_receipts","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_receipts\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"expense_id\\" UUID NOT NULL,\\n \\"asset_id\\" UUID NOT NULL,\\n \\"uploaded_by_profile_id\\" UUID,\\n \\"filename\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"byte_count\\" INTEGER,\\n \\"content_sha256\\" TEXT NOT NULL\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"CASCADE","onUpdate":"CASCADE"}},"asset_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"uploaded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"filename":{"type":"TEXT","notNull":false,"unique":false},"mime_type":{"type":"TEXT","notNull":false,"unique":false},"byte_count":{"type":"INTEGER","notNull":false,"unique":false},"content_sha256":{"type":"TEXT","notNull":true,"unique":false}},"indexes":[{"name":"expense_receipts_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expense_receipts_expense_sha256_key","columns":["expense_id","content_sha256"],"unique":true},{"name":"expense_receipts_tenant_sha256_idx","columns":["tenant_id","content_sha256"]},{"name":"expense_receipts_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_receipts_asset_id_idx","columns":["asset_id"]},{"name":"expense_receipts_uploaded_by_profile_id_idx","columns":["uploaded_by_profile_id"]}],"version":"8ed688ee"},"importPath":"@happyvertical/smrt-expenses"}},"moduleType":"smrt","recipes":[{"id":"expenses.tracking","className":"ExpensesRecipe","label":"Expenses","summary":"Record what the business spends, with receipts, and review it.","synonyms":["costs","receipts","spending","reimbursements"],"models":["@happyvertical/smrt-expenses:Expense","@happyvertical/smrt-expenses:ExpenseReceipt"],"nav":[{"label":"Expenses","model":"@happyvertical/smrt-expenses:Expense","icon":"receipt","description":"Costs you have paid, with the receipt and who paid."}],"requires":[],"section":{"id":"accounting","label":"Accounting","icon":"bank","description":"Your books: accounts, journals and balances."},"options":{"@happyvertical/smrt-expenses:Expense":{"fields":{"description":{"order":1,"required":true},"amount":{"order":2},"currency":{"order":3},"incurredOn":{"label":"Date","order":4,"help":"The date on the receipt, as YYYY-MM-DD."},"category":{"order":5,"help":"One word, such as material, travel, freight or other."},"vendorId":{"label":"Vendor","order":6},"paidBy":{"order":7},"paidByProfileId":{"label":"Person who paid","order":8,"help":"Who paid out of their own pocket, when a person paid."},"paidByName":{"order":9},"reimbursable":{"order":10},"costObjectId":{"label":"Charged to","order":11,"help":"The job, project or client the cost belongs to."},"costObjectType":{"visibility":"hidden"},"financialBreakdown":{"visibility":"hidden"},"supplierPayableJson":{"visibility":"hidden"},"refundRecipient":{"visibility":"hidden"},"reimbursedAmount":{"help":"The recorded amount paid back to the person who paid."},"refundOfId":{"visibility":"hidden"},"recordedByProfileId":{"visibility":"hidden"},"commitmentId":{"visibility":"hidden"},"commitmentLineId":{"visibility":"hidden"},"reviewedByProfileId":{"visibility":"hidden"},"duplicateOfId":{"visibility":"hidden"}}},"@happyvertical/smrt-expenses:ExpenseReceipt":{"fields":{"contentSha256":{"visibility":"hidden"},"byteCount":{"visibility":"hidden"},"uploadedByProfileId":{"visibility":"hidden"}}}},"help":{"markdown":"## Overview\\n\\nExpenses are the costs the business pays: a receipt, who paid it and what it\\nwas for. Each expense is reviewed once, and a reimbursable one is marked when\\nthe person is paid back.\\n\\n## Tasks\\n\\n### Record an expense\\n\\n1. Create a new expense and write a short **{field:description}**.\\n2. Enter the **{field:amount}** and its **{field:currency}**.\\n3. Set the **{field:incurredOn}** date from the receipt and pick a\\n **{field:category}**.\\n4. Choose who **{field:paidBy}**: the company, a person or a third party.\\n5. When a person paid, pick the **{field:paidByProfileId}** and tick\\n **{field:reimbursable}** when they are owed the money back.\\n\\n### Review it\\n\\n1. Open the expense and check the receipt attached to it.\\n2. **{field:reviewStatus}** moves from unreviewed to reviewed, or to rejected,\\n when someone reviews it. A rejected expense can be reopened.\\n","fieldRefs":["amount","category","currency","description","incurredOn","paidBy","paidByProfileId","reimbursable","reviewStatus"]},"demo":{"mode":"server","reasons":["Needs a server because its package does not build for a browser: node:crypto; and its package depends on @happyvertical/smrt-agents, @happyvertical/smrt-commerce, @happyvertical/smrt-jobs, @happyvertical/smrt-ledgers, @happyvertical/smrt-products, @happyvertical/smrt-profiles, @happyvertical/smrt-reports, @happyvertical/smrt-secrets, @happyvertical/smrt-subscriptions, @happyvertical/smrt-tenancy, @happyvertical/smrt-users, which do not build for a browser (#3615, #3616, #3617, #3624, #3625, #3626, #3627)."]}}],"smrtDependencies":["@happyvertical/smrt-assets","@happyvertical/smrt-commerce","@happyvertical/smrt-core","@happyvertical/smrt-tenancy","@happyvertical/smrt-users"],"browser":{"status":"server-only","issues":["#3615","#3616","#3617","#3624","#3625","#3626","#3627"],"reason":"node:crypto","via":["@happyvertical/smrt-agents","@happyvertical/smrt-commerce","@happyvertical/smrt-jobs","@happyvertical/smrt-ledgers","@happyvertical/smrt-products","@happyvertical/smrt-profiles","@happyvertical/smrt-reports","@happyvertical/smrt-secrets","@happyvertical/smrt-subscriptions","@happyvertical/smrt-tenancy","@happyvertical/smrt-users"]},"stampsConstructors":true}`));
11
+ ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-expenses","packageVersion":"0.60.15","objects":{"@happyvertical/smrt-expenses:ExpenseCollection":{"name":"expensecollection","className":"ExpenseCollection","qualifiedName":"@happyvertical/smrt-expenses:ExpenseCollection","collection":"expenses","description":"Collection for Expense. Reads go through the normal tenant interceptors, so inside withTenant() every method here sees only that tenant's expenses.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/collections/ExpenseCollection.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{"recordSupplierPayable":{"name":"recordSupplierPayable","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"RecordSupplierPayableInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"recordSupplierPayableEvent":{"name":"recordSupplierPayableEvent","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"RecordSupplierPayableEventInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"completeSupplierPayableBasis":{"name":"completeSupplierPayableBasis","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"CompleteSupplierPayableBasisInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"supplierPayablePosition":{"name":"supplierPayablePosition","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false},{"name":"input","type":"object","optional":true,"memberTypes":["string"]}],"returnType":"Promise<SupplierPayablePosition | null>","isStatic":false,"isPublic":true},"reimbursementPosition":{"name":"reimbursementPosition","async":true,"parameters":[{"name":"sourceId","type":"string","optional":false}],"returnType":"Promise<ExpenseReimbursementPosition>","isStatic":false,"isPublic":true},"refundPosition":{"name":"refundPosition","async":true,"parameters":[{"name":"sourceId","type":"string","optional":false}],"returnType":"Promise<ExpenseRefundPosition>","isStatic":false,"isPublic":true},"record":{"name":"record","async":true,"parameters":[{"name":"options","type":"RecordExpenseActionInput","optional":false}],"returnType":"Promise<Expense>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"record","effect":"write","idempotent":false,"openWorld":false,"description":"Record a new expense for review (service-backed)."}},"attachReceipt":{"name":"attachReceipt","async":true,"parameters":[{"name":"options","type":"AttachExpenseReceiptActionInput","optional":false}],"returnType":"Promise<ExpenseReceipt>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"attachReceipt","effect":"write","idempotent":false,"openWorld":false,"description":"Attach a receipt file to an expense (service-backed)."}},"recordReview":{"name":"recordReview","async":true,"parameters":[{"name":"options","type":"ReviewExpenseActionInput","optional":false}],"returnType":"Promise<Expense>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"recordReview","effect":"write","idempotent":false,"openWorld":false,"description":"Approve or reject an expense with a reason (service-backed)."}},"recordReimbursement":{"name":"recordReimbursement","async":true,"parameters":[{"name":"options","type":"MarkExpenseReimbursedActionInput","optional":false}],"returnType":"Promise<Expense>","isStatic":false,"isPublic":true,"decoratorConfig":{"httpMethod":"POST","path":"recordReimbursement","effect":"write","idempotent":false,"openWorld":false,"description":"Record that an expense was reimbursed (service-backed)."}},"forCostObject":{"name":"forCostObject","async":true,"parameters":[{"name":"costObjectType","type":"string","optional":false},{"name":"costObjectId","type":"string","optional":false}],"returnType":"Promise<Expense[]>","isStatic":false,"isPublic":true},"totalsByCurrency":{"name":"totalsByCurrency","async":true,"parameters":[{"name":"options","type":"ExpenseTotalsOptions","optional":true}],"returnType":"Promise<CurrencyTotals>","isStatic":false,"isPublic":true},"reviewAllocation":{"name":"reviewAllocation","async":true,"parameters":[{"name":"input","type":"ReviewExpenseAllocationInput","optional":false},{"name":"authority","type":"ExpenseAllocationAuthority","optional":false}],"returnType":"Promise<ExpenseAllocationResult>","isStatic":false,"isPublic":true},"withdrawAllocation":{"name":"withdrawAllocation","async":true,"parameters":[{"name":"input","type":"WithdrawExpenseAllocationInput","optional":false},{"name":"authority","type":"ExpenseAllocationAuthority","optional":false}],"returnType":"Promise<WithdrawExpenseAllocationResult>","isStatic":false,"isPublic":true},"listReviewedTaxFacts":{"name":"listReviewedTaxFacts","async":true,"parameters":[{"name":"options","type":"ReviewedExpenseTaxFactsOptions","optional":false}],"returnType":"Promise<ReviewedExpenseTaxFact[]>","isStatic":false,"isPublic":true},"commitmentPosition":{"name":"commitmentPosition","async":true,"parameters":[{"name":"commitmentId","type":"string","optional":false},{"name":"options","type":"object","optional":true,"memberTypes":["string"]}],"returnType":"Promise<CommitmentPosition>","isStatic":false,"isPublic":true}},"decoratorConfig":{"api":{"include":["record","attachReceipt","recordReview","recordReimbursement"]},"mcp":{"include":[]},"cli":false,"tableName":"expenses"},"extends":"SmrtCollection","extendsTypeArg":"Expense","exportName":"ExpenseCollection","collectionExportName":"ExpenseCollectionCollection","collectionOf":"@happyvertical/smrt-expenses:Expense","schema":{"tableName":"expenses","ddl":"CREATE TABLE IF NOT EXISTS \\"expenses\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"cost_object_type\\" TEXT,\\n \\"cost_object_id\\" TEXT,\\n \\"category\\" TEXT,\\n \\"description\\" TEXT,\\n \\"amount\\" INTEGER,\\n \\"currency\\" TEXT,\\n \\"financial_breakdown\\" TEXT,\\n \\"supplier_payable_json\\" TEXT,\\n \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\n \\"refund_recipient\\" TEXT,\\n \\"incurred_on\\" TEXT,\\n \\"recorded_at\\" TIMESTAMP,\\n \\"recorded_by_profile_id\\" UUID,\\n \\"paid_by\\" TEXT,\\n \\"paid_by_profile_id\\" UUID,\\n \\"vendor_id\\" UUID,\\n \\"commitment_id\\" UUID,\\n \\"commitment_line_id\\" UUID,\\n \\"review_status\\" TEXT,\\n \\"reviewed_by_profile_id\\" UUID,\\n \\"reviewed_at\\" TIMESTAMP,\\n \\"review_note\\" TEXT,\\n \\"duplicate_of_id\\" UUID,\\n \\"reimbursable\\" BOOLEAN,\\n \\"reimbursed_amount\\" INTEGER,\\n \\"reimbursed_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT,\\n \\"active_allocation_revision_id\\" UUID\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"category":{"type":"TEXT","notNull":false,"unique":false},"description":{"type":"TEXT","notNull":false,"unique":false},"amount":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false},"financial_breakdown":{"type":"TEXT","notNull":false,"unique":false},"supplier_payable_json":{"type":"TEXT","notNull":false,"unique":false},"paid_by_name":{"type":"TEXT","notNull":false,"unique":false},"refund_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"refund_recipient":{"type":"TEXT","notNull":false,"unique":false},"incurred_on":{"type":"TEXT","notNull":false,"unique":false},"recorded_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"recorded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"paid_by":{"type":"TEXT","notNull":false,"unique":false},"paid_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"vendor_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_line_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"review_status":{"type":"TEXT","notNull":false,"unique":false},"reviewed_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"reviewed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"review_note":{"type":"TEXT","notNull":false,"unique":false},"duplicate_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"SET NULL","onUpdate":"CASCADE"}},"reimbursable":{"type":"BOOLEAN","notNull":false,"unique":false},"reimbursed_amount":{"type":"INTEGER","notNull":false,"unique":false},"reimbursed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"reimbursement_reference":{"type":"TEXT","notNull":false,"unique":false},"active_allocation_revision_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expense_allocation_revisions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}}},"indexes":[{"name":"expenses_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expenses_cost_object_idx","columns":["tenant_id","cost_object_type","cost_object_id","incurred_on"]},{"name":"expenses_commitment_review_idx","columns":["tenant_id","commitment_id","review_status"]},{"name":"expenses_review_status_idx","columns":["tenant_id","review_status","incurred_on"]},{"name":"expenses_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expenses_refund_of_id_idx","columns":["refund_of_id"]},{"name":"expenses_recorded_by_profile_id_idx","columns":["recorded_by_profile_id"]},{"name":"expenses_paid_by_profile_id_idx","columns":["paid_by_profile_id"]},{"name":"expenses_vendor_id_idx","columns":["vendor_id"]},{"name":"expenses_commitment_id_idx","columns":["commitment_id"]},{"name":"expenses_commitment_line_id_idx","columns":["commitment_line_id"]},{"name":"expenses_reviewed_by_profile_id_idx","columns":["reviewed_by_profile_id"]},{"name":"expenses_duplicate_of_id_idx","columns":["duplicate_of_id"]},{"name":"expenses_active_allocation_revision_id_idx","columns":["active_allocation_revision_id"]}],"version":"f8413307"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseReceiptCollection":{"name":"expensereceiptcollection","className":"ExpenseReceiptCollection","qualifiedName":"@happyvertical/smrt-expenses:ExpenseReceiptCollection","collection":"expensereceipts","description":"Junction collection over expense_receipts (left: expense, right: asset). Reads go through the normal tenant interceptors.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/collections/ExpenseReceiptCollection.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{"byRight":{"name":"byRight","async":true,"parameters":[{"name":"rightId","type":"string","optional":false},{"name":"opts","type":"JunctionFilterOptions","optional":true}],"returnType":"Promise<TItem[]>","isStatic":false,"isPublic":true},"byLeft":{"name":"byLeft","async":true,"parameters":[{"name":"leftId","type":"string","optional":false},{"name":"opts","type":"JunctionFilterOptions","optional":true}],"returnType":"Promise<TItem[]>","isStatic":false,"isPublic":true},"attach":{"name":"attach","async":true,"parameters":[{"name":"_expenseId","type":"string","optional":false},{"name":"_assetId","type":"string","optional":false},{"name":"_opts","type":"JunctionAttachOptions","optional":true}],"returnType":"Promise<ExpenseReceipt>","isStatic":false,"isPublic":true},"detach":{"name":"detach","async":true,"parameters":[{"name":"leftId","type":"string","optional":false},{"name":"rightId","type":"string","optional":false},{"name":"opts","type":"JunctionFilterOptions","optional":true}],"returnType":"Promise<void>","isStatic":false,"isPublic":true},"setLinks":{"name":"setLinks","async":true,"parameters":[{"name":"_expenseId","type":"string","optional":false},{"name":"_assetIds","type":"string[]","optional":false},{"name":"_opts","type":"JunctionAttachOptions","optional":true}],"returnType":"Promise<void>","isStatic":false,"isPublic":true},"attachReceipt":{"name":"attachReceipt","async":true,"parameters":[{"name":"input","type":"AttachReceiptInput","optional":false}],"returnType":"Promise<ExpenseReceipt>","isStatic":false,"isPublic":true},"receiptsFor":{"name":"receiptsFor","async":true,"parameters":[{"name":"expenseId","type":"string","optional":false}],"returnType":"Promise<ExpenseReceipt[]>","isStatic":false,"isPublic":true},"findDuplicateReceipts":{"name":"findDuplicateReceipts","async":true,"parameters":[{"name":"options","type":"FindDuplicateReceiptsOptions","optional":true}],"returnType":"Promise<DuplicateReceiptGroup[]>","isStatic":false,"isPublic":true}},"decoratorConfig":{"api":{"include":[]},"mcp":{"include":[]},"cli":false,"tableName":"expense_receipts"},"extends":"SmrtJunction","extendsTypeArg":"ExpenseReceipt","exportName":"ExpenseReceiptCollection","collectionExportName":"ExpenseReceiptCollectionCollection","collectionOf":"@happyvertical/smrt-expenses:ExpenseReceipt","schema":{"tableName":"expense_receipts","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_receipts\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"expense_id\\" UUID NOT NULL,\\n \\"asset_id\\" UUID NOT NULL,\\n \\"uploaded_by_profile_id\\" UUID,\\n \\"filename\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"byte_count\\" INTEGER,\\n \\"content_sha256\\" TEXT NOT NULL\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"CASCADE","onUpdate":"CASCADE"}},"asset_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"uploaded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"filename":{"type":"TEXT","notNull":false,"unique":false},"mime_type":{"type":"TEXT","notNull":false,"unique":false},"byte_count":{"type":"INTEGER","notNull":false,"unique":false},"content_sha256":{"type":"TEXT","notNull":true,"unique":false}},"indexes":[{"name":"expense_receipts_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expense_receipts_expense_sha256_key","columns":["expense_id","content_sha256"],"unique":true},{"name":"expense_receipts_tenant_sha256_idx","columns":["tenant_id","content_sha256"]},{"name":"expense_receipts_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_receipts_asset_id_idx","columns":["asset_id"]},{"name":"expense_receipts_uploaded_by_profile_id_idx","columns":["uploaded_by_profile_id"]}],"version":"8ed688ee"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:Revisions":{"name":"revisions","className":"Revisions","qualifiedName":"@happyvertical/smrt-expenses:Revisions","collection":"expenseallocationrevisions","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/expense-allocation.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{},"decoratorConfig":{"api":{"include":[]},"mcp":{"include":[]},"cli":false,"tableName":"expense_allocation_revisions"},"extends":"SmrtCollection","extendsTypeArg":"ExpenseAllocationRevision","exportName":"Revisions","collectionExportName":"RevisionsCollection","collectionOf":"@happyvertical/smrt-expenses:ExpenseAllocationRevision","schema":{"tableName":"expense_allocation_revisions","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_allocation_revisions\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"kind\\" TEXT DEFAULT 'allocation',\\n \\"source_cost_object_type\\" TEXT,\\n \\"source_cost_object_id\\" TEXT,\\n \\"incurred_on\\" TEXT,\\n \\"paid_by\\" TEXT DEFAULT 'company',\\n \\"reimbursable\\" BOOLEAN NOT NULL DEFAULT FALSE,\\n \\"has_supplier_tax\\" BOOLEAN,\\n \\"basis_equals_gross\\" BOOLEAN NOT NULL DEFAULT FALSE,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"source_expense_id\\" UUID NOT NULL,\\n \\"previous_revision_id\\" UUID,\\n \\"request_id\\" TEXT,\\n \\"request_fingerprint\\" TEXT,\\n \\"source_fingerprint\\" TEXT,\\n \\"source_fingerprint_nonce\\" TEXT,\\n \\"source_revision\\" TEXT,\\n \\"amount_minor\\" INTEGER,\\n \\"currency\\" TEXT,\\n \\"reviewed_by_profile_id\\" UUID NOT NULL,\\n \\"reviewed_by_user_id\\" UUID NOT NULL,\\n \\"review_evidence\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"kind":{"type":"TEXT","notNull":false,"unique":false,"default":"allocation"},"source_cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"source_cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"incurred_on":{"type":"TEXT","notNull":false,"unique":false},"paid_by":{"type":"TEXT","notNull":false,"unique":false,"default":"company"},"reimbursable":{"type":"BOOLEAN","notNull":true,"unique":false,"default":false},"has_supplier_tax":{"type":"BOOLEAN","notNull":false,"unique":false},"basis_equals_gross":{"type":"BOOLEAN","notNull":true,"unique":false,"default":false},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"source_expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"previous_revision_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expense_allocation_revisions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"request_id":{"type":"TEXT","notNull":false,"unique":false},"request_fingerprint":{"type":"TEXT","notNull":false,"unique":false},"source_fingerprint":{"type":"TEXT","notNull":false,"unique":false},"source_fingerprint_nonce":{"type":"TEXT","notNull":false,"unique":false},"source_revision":{"type":"TEXT","notNull":false,"unique":false},"amount_minor":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false},"reviewed_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reviewed_by_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"review_evidence":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expense_allocation_revisions_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"expense_allocation_request_key","columns":["source_expense_id","request_id"],"unique":true},{"name":"expense_allocation_revision_successor","columns":["previous_revision_id"],"unique":true},{"name":"expense_allocation_revisions_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_allocation_revisions_reviewed_by_profile_id_idx","columns":["reviewed_by_profile_id"]},{"name":"expense_allocation_revisions_reviewed_by_user_id_idx","columns":["reviewed_by_user_id"]}],"version":"83425a51"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:Portions":{"name":"portions","className":"Portions","qualifiedName":"@happyvertical/smrt-expenses:Portions","collection":"expenseallocationportions","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/expense-allocation.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{},"decoratorConfig":{"api":{"include":[]},"mcp":{"include":[]},"cli":false,"tableName":"expense_allocation_portions"},"extends":"SmrtCollection","extendsTypeArg":"ExpenseAllocationPortion","exportName":"Portions","collectionExportName":"PortionsCollection","collectionOf":"@happyvertical/smrt-expenses:ExpenseAllocationPortion","schema":{"tableName":"expense_allocation_portions","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_allocation_portions\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"source_expense_id\\" UUID NOT NULL,\\n \\"revision_id\\" UUID NOT NULL,\\n \\"portion_key\\" TEXT,\\n \\"cost_object_type\\" TEXT,\\n \\"cost_object_id\\" TEXT,\\n \\"cost_code_ref\\" TEXT,\\n \\"amount_minor\\" INTEGER,\\n \\"currency\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"source_expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"revision_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expense_allocation_revisions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"portion_key":{"type":"TEXT","notNull":false,"unique":false},"cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"cost_code_ref":{"type":"TEXT","notNull":false,"unique":false},"amount_minor":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expense_allocation_portions_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"expense_allocation_portion_key","columns":["revision_id","portion_key"],"unique":true},{"name":"expense_allocation_portions_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_allocation_portions_source_expense_id_idx","columns":["source_expense_id"]}],"version":"6b0600b1"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:Billings":{"name":"billings","className":"Billings","qualifiedName":"@happyvertical/smrt-expenses:Billings","collection":"expenseallocationbillings","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/expense-allocation.ts","packageName":"@happyvertical/smrt-expenses","fields":{},"methods":{},"decoratorConfig":{"api":{"include":[]},"mcp":{"include":[]},"cli":false,"tableName":"expense_allocation_billings"},"extends":"SmrtCollection","extendsTypeArg":"ExpenseAllocationBilling","exportName":"Billings","collectionExportName":"BillingsCollection","collectionOf":"@happyvertical/smrt-expenses:ExpenseAllocationBilling","schema":{"tableName":"expense_allocation_billings","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_allocation_billings\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"source_expense_id\\" UUID NOT NULL,\\n \\"portion_id\\" UUID NOT NULL,\\n \\"invoice_id\\" UUID,\\n \\"draft_type\\" TEXT,\\n \\"draft_id\\" TEXT,\\n \\"invoice_line_id\\" UUID,\\n \\"request_id\\" TEXT,\\n \\"request_fingerprint\\" TEXT,\\n \\"related_reservation_id\\" UUID,\\n \\"kind\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"source_expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"portion_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expense_allocation_portions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"invoice_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"draft_type":{"type":"TEXT","notNull":false,"unique":false},"draft_id":{"type":"TEXT","notNull":false,"unique":false},"invoice_line_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"request_id":{"type":"TEXT","notNull":false,"unique":false},"request_fingerprint":{"type":"TEXT","notNull":false,"unique":false},"related_reservation_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expense_allocation_billings","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"kind":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expense_allocation_billings_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"expense_allocation_billing_request","columns":["source_expense_id","request_id"],"unique":true},{"name":"expense_allocation_billing_release","columns":["related_reservation_id","kind"],"unique":true},{"name":"expense_allocation_billings_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_allocation_billings_portion_id_idx","columns":["portion_id"]},{"name":"expense_allocation_billings_invoice_id_idx","columns":["invoice_id"]},{"name":"expense_allocation_billings_invoice_line_id_idx","columns":["invoice_line_id"]}],"version":"432aeb1f"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:Expense":{"name":"expense","className":"Expense","qualifiedName":"@happyvertical/smrt-expenses:Expense","collection":"expenses","description":"A cost someone incurred.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/Expense.ts","packageName":"@happyvertical/smrt-expenses","fields":{"tenantId":{"type":"text","required":false,"_meta":{"sqlType":"UUID","nullable":true,"__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":false,"autoPopulate":true,"nullable":true,"mode":"optional","field":"tenantId","allowSuperAdminBypass":false}}},"costObjectType":{"type":"text","required":false,"_meta":{}},"costObjectId":{"type":"text","required":false,"_meta":{}},"category":{"type":"text","required":false,"_meta":{}},"description":{"type":"text","required":false,"_meta":{}},"amount":{"type":"integer","required":false,"_meta":{}},"currency":{"type":"text","required":false,"_meta":{}},"financialBreakdown":{"type":"text","required":false,"_meta":{}},"supplierPayableJson":{"type":"text","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"paidByName":{"type":"text","required":false,"_meta":{}},"refundOfId":{"type":"foreignKey","required":false,"related":"Expense","_meta":{"onDelete":"RESTRICT"}},"refundRecipient":{"type":"text","required":false,"_meta":{}},"incurredOn":{"type":"text","required":false,"_meta":{}},"recordedAt":{"type":"datetime","required":false,"_meta":{}},"recordedByProfileId":{"type":"crossPackageRef","required":false,"related":"PROFILE"},"paidBy":{"type":"text","required":false,"_meta":{},"enum":["company","person","third-party"]},"paidByProfileId":{"type":"crossPackageRef","required":false,"related":"PROFILE"},"vendorId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:Vendor"},"commitmentId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:PurchaseContract","_meta":{"validateSubtype":true}},"commitmentLineId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:ContractLineItem"},"reviewStatus":{"type":"text","required":false,"_meta":{"readonly":true},"enum":["unreviewed","reviewed","rejected"],"readonly":true},"reviewedByProfileId":{"type":"crossPackageRef","required":false,"related":"PROFILE"},"reviewedAt":{"type":"datetime","required":false,"_meta":{"readonly":true},"readonly":true},"reviewNote":{"type":"text","required":false,"_meta":{"readonly":true},"readonly":true},"duplicateOfId":{"type":"foreignKey","required":false,"related":"Expense","_meta":{"onDelete":"SET NULL"}},"reimbursable":{"type":"boolean","required":false,"_meta":{}},"reimbursedAmount":{"type":"integer","required":false,"_meta":{"nullable":true,"readonly":true},"readonly":true},"reimbursedAt":{"type":"datetime","required":false,"_meta":{"readonly":true},"readonly":true},"reimbursementReference":{"type":"text","required":false,"_meta":{"readonly":true},"readonly":true},"activeAllocationRevisionId":{"type":"foreignKey","required":false,"related":"ExpenseAllocationRevision","_meta":{"readonly":true,"nullable":true,"onDelete":"RESTRICT"},"readonly":true}},"methods":{"getFinancialBreakdown":{"name":"getFinancialBreakdown","async":false,"parameters":[],"returnType":"ExpenseFinancialBreakdown | null","isStatic":false,"isPublic":true},"setFinancialBreakdown":{"name":"setFinancialBreakdown","async":false,"parameters":[{"name":"value","type":"ExpenseFinancialBreakdown | null","optional":false,"unionBranches":[{"type":"ExpenseFinancialBreakdown"},{"type":"null"}]}],"returnType":"void","isStatic":false,"isPublic":true},"getRefundRecipient":{"name":"getRefundRecipient","async":false,"parameters":[],"returnType":"ExpenseRefundRecipient | null","isStatic":false,"isPublic":true},"setRefundRecipient":{"name":"setRefundRecipient","async":false,"parameters":[{"name":"value","type":"ExpenseRefundRecipient | null","optional":false,"unionBranches":[{"type":"ExpenseRefundRecipient"},{"type":"null"}]}],"returnType":"void","isStatic":false,"isPublic":true},"getCostBasis":{"name":"getCostBasis","async":false,"parameters":[],"returnType":"ExpenseCostBasis | null","isStatic":false,"isPublic":true},"isReimbursed":{"name":"isReimbursed","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"isCountable":{"name":"isCountable","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"review":{"name":"review","async":true,"parameters":[{"name":"input","type":"ReviewExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"reject":{"name":"reject","async":true,"parameters":[{"name":"input","type":"RejectExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"markDuplicate":{"name":"markDuplicate","async":true,"parameters":[{"name":"input","type":"MarkDuplicateExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"reopen":{"name":"reopen","async":true,"parameters":[{"name":"input","type":"ReopenExpenseInput","optional":false}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"markReimbursed":{"name":"markReimbursed","async":true,"parameters":[{"name":"input","type":"MarkReimbursedInput","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"getCostObject":{"name":"getCostObject","async":true,"parameters":[],"returnType":"Promise<T | null>","isStatic":false,"isPublic":true},"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"recordSupplierPayable":{"name":"recordSupplierPayable","async":true,"parameters":[{"name":"input","type":"RecordSupplierPayableInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"recordSupplierPayableEvent":{"name":"recordSupplierPayableEvent","async":true,"parameters":[{"name":"input","type":"RecordSupplierPayableEventInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"completeSupplierPayableBasis":{"name":"completeSupplierPayableBasis","async":true,"parameters":[{"name":"input","type":"CompleteSupplierPayableBasisInput","optional":false}],"returnType":"Promise<SupplierPayablePosition>","isStatic":false,"isPublic":true},"getSupplierPayablePosition":{"name":"getSupplierPayablePosition","async":true,"parameters":[{"name":"input","type":"object","optional":true,"memberTypes":["string"]}],"returnType":"Promise<SupplierPayablePosition | null>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[{"name":"options","type":"object","optional":true,"memberTypes":["Date | string"]}],"returnType":"Promise<void>","isStatic":false,"isPublic":true},"getReimbursementPosition":{"name":"getReimbursementPosition","async":true,"parameters":[],"returnType":"Promise<ExpenseReimbursementPosition>","isStatic":false,"isPublic":true},"getRefundPosition":{"name":"getRefundPosition","async":true,"parameters":[],"returnType":"Promise<ExpenseRefundPosition>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expenses","api":{"include":["record","attachReceipt","recordReview","recordReimbursement"]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expenses_cost_object_idx","columns":["tenantId","costObjectType","costObjectId","incurredOn"]},{"name":"expenses_commitment_review_idx","columns":["tenantId","commitmentId","reviewStatus"]},{"name":"expenses_review_status_idx","columns":["tenantId","reviewStatus","incurredOn"]}],"tenantScoped":{"mode":"optional"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"Expense","collectionExportName":"ExpenseCollection","schema":{"tableName":"expenses","ddl":"CREATE TABLE IF NOT EXISTS \\"expenses\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"cost_object_type\\" TEXT,\\n \\"cost_object_id\\" TEXT,\\n \\"category\\" TEXT,\\n \\"description\\" TEXT,\\n \\"amount\\" INTEGER,\\n \\"currency\\" TEXT,\\n \\"financial_breakdown\\" TEXT,\\n \\"supplier_payable_json\\" TEXT,\\n \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\n \\"refund_recipient\\" TEXT,\\n \\"incurred_on\\" TEXT,\\n \\"recorded_at\\" TIMESTAMP,\\n \\"recorded_by_profile_id\\" UUID,\\n \\"paid_by\\" TEXT,\\n \\"paid_by_profile_id\\" UUID,\\n \\"vendor_id\\" UUID,\\n \\"commitment_id\\" UUID,\\n \\"commitment_line_id\\" UUID,\\n \\"review_status\\" TEXT,\\n \\"reviewed_by_profile_id\\" UUID,\\n \\"reviewed_at\\" TIMESTAMP,\\n \\"review_note\\" TEXT,\\n \\"duplicate_of_id\\" UUID,\\n \\"reimbursable\\" BOOLEAN,\\n \\"reimbursed_amount\\" INTEGER,\\n \\"reimbursed_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT,\\n \\"active_allocation_revision_id\\" UUID\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"category":{"type":"TEXT","notNull":false,"unique":false},"description":{"type":"TEXT","notNull":false,"unique":false},"amount":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false},"financial_breakdown":{"type":"TEXT","notNull":false,"unique":false},"supplier_payable_json":{"type":"TEXT","notNull":false,"unique":false},"paid_by_name":{"type":"TEXT","notNull":false,"unique":false},"refund_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"refund_recipient":{"type":"TEXT","notNull":false,"unique":false},"incurred_on":{"type":"TEXT","notNull":false,"unique":false},"recorded_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"recorded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"paid_by":{"type":"TEXT","notNull":false,"unique":false},"paid_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"vendor_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"commitment_line_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"review_status":{"type":"TEXT","notNull":false,"unique":false},"reviewed_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"reviewed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"review_note":{"type":"TEXT","notNull":false,"unique":false},"duplicate_of_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"SET NULL","onUpdate":"CASCADE"}},"reimbursable":{"type":"BOOLEAN","notNull":false,"unique":false},"reimbursed_amount":{"type":"INTEGER","notNull":false,"unique":false},"reimbursed_at":{"type":"TIMESTAMP","notNull":false,"unique":false},"reimbursement_reference":{"type":"TEXT","notNull":false,"unique":false},"active_allocation_revision_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expense_allocation_revisions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}}},"indexes":[{"name":"expenses_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expenses_cost_object_idx","columns":["tenant_id","cost_object_type","cost_object_id","incurred_on"]},{"name":"expenses_commitment_review_idx","columns":["tenant_id","commitment_id","review_status"]},{"name":"expenses_review_status_idx","columns":["tenant_id","review_status","incurred_on"]},{"name":"expenses_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expenses_refund_of_id_idx","columns":["refund_of_id"]},{"name":"expenses_recorded_by_profile_id_idx","columns":["recorded_by_profile_id"]},{"name":"expenses_paid_by_profile_id_idx","columns":["paid_by_profile_id"]},{"name":"expenses_vendor_id_idx","columns":["vendor_id"]},{"name":"expenses_commitment_id_idx","columns":["commitment_id"]},{"name":"expenses_commitment_line_id_idx","columns":["commitment_line_id"]},{"name":"expenses_reviewed_by_profile_id_idx","columns":["reviewed_by_profile_id"]},{"name":"expenses_duplicate_of_id_idx","columns":["duplicate_of_id"]},{"name":"expenses_active_allocation_revision_id_idx","columns":["active_allocation_revision_id"]}],"version":"f8413307"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseAllocationRevision":{"name":"expenseallocationrevision","className":"ExpenseAllocationRevision","qualifiedName":"@happyvertical/smrt-expenses:ExpenseAllocationRevision","collection":"expenseallocationrevisions","description":"Persisted command-owned ExpenseAllocationRevision evidence.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/ExpenseAllocation.ts","packageName":"@happyvertical/smrt-expenses","fields":{"kind":{"type":"text","required":false,"default":"allocation","enum":["allocation","withdrawal"]},"sourceCostObjectType":{"type":"text","required":false},"sourceCostObjectId":{"type":"text","required":false},"incurredOn":{"type":"text","required":false},"paidBy":{"type":"text","required":false,"default":"company","enum":["company","person","third-party"]},"reimbursable":{"type":"boolean","required":true,"default":false},"hasSupplierTax":{"type":"boolean","required":false,"_meta":{"nullable":true}},"basisEqualsGross":{"type":"boolean","required":true,"default":false},"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"sourceExpenseId":{"type":"foreignKey","required":true,"related":"Expense","_meta":{"required":true,"onDelete":"RESTRICT"}},"previousRevisionId":{"type":"foreignKey","required":false,"related":"ExpenseAllocationRevision","_meta":{"nullable":true,"onDelete":"RESTRICT"}},"requestId":{"type":"text","required":false},"requestFingerprint":{"type":"text","required":false},"sourceFingerprint":{"type":"text","required":false},"sourceFingerprintNonce":{"type":"text","required":false},"sourceRevision":{"type":"text","required":false},"amountMinor":{"type":"integer","required":false,"_meta":{}},"currency":{"type":"text","required":false},"reviewedByProfileId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-profiles:Profile"},"reviewedByUserId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:User"},"reviewEvidence":{"type":"text","required":false}},"methods":{"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[],"returnType":"Promise<void>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expense_allocation_revisions","api":{"include":[]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expense_allocation_request_key","columns":["sourceExpenseId","requestId"],"unique":true},{"name":"expense_allocation_revision_successor","columns":["previousRevisionId"],"unique":true}],"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ExpenseAllocationRevision","collectionExportName":"ExpenseAllocationRevisionCollection","validationRules":[{"field":"reimbursable","rule":"required","fieldType":"boolean"},{"field":"basisEqualsGross","rule":"required","fieldType":"boolean"},{"field":"sourceExpenseId","rule":"required","fieldType":"foreignKey"},{"field":"reviewedByProfileId","rule":"required","fieldType":"crossPackageRef"},{"field":"reviewedByUserId","rule":"required","fieldType":"crossPackageRef"}],"schema":{"tableName":"expense_allocation_revisions","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_allocation_revisions\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"kind\\" TEXT DEFAULT 'allocation',\\n \\"source_cost_object_type\\" TEXT,\\n \\"source_cost_object_id\\" TEXT,\\n \\"incurred_on\\" TEXT,\\n \\"paid_by\\" TEXT DEFAULT 'company',\\n \\"reimbursable\\" BOOLEAN NOT NULL DEFAULT FALSE,\\n \\"has_supplier_tax\\" BOOLEAN,\\n \\"basis_equals_gross\\" BOOLEAN NOT NULL DEFAULT FALSE,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"source_expense_id\\" UUID NOT NULL,\\n \\"previous_revision_id\\" UUID,\\n \\"request_id\\" TEXT,\\n \\"request_fingerprint\\" TEXT,\\n \\"source_fingerprint\\" TEXT,\\n \\"source_fingerprint_nonce\\" TEXT,\\n \\"source_revision\\" TEXT,\\n \\"amount_minor\\" INTEGER,\\n \\"currency\\" TEXT,\\n \\"reviewed_by_profile_id\\" UUID NOT NULL,\\n \\"reviewed_by_user_id\\" UUID NOT NULL,\\n \\"review_evidence\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"kind":{"type":"TEXT","notNull":false,"unique":false,"default":"allocation"},"source_cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"source_cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"incurred_on":{"type":"TEXT","notNull":false,"unique":false},"paid_by":{"type":"TEXT","notNull":false,"unique":false,"default":"company"},"reimbursable":{"type":"BOOLEAN","notNull":true,"unique":false,"default":false},"has_supplier_tax":{"type":"BOOLEAN","notNull":false,"unique":false},"basis_equals_gross":{"type":"BOOLEAN","notNull":true,"unique":false,"default":false},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"source_expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"previous_revision_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expense_allocation_revisions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"request_id":{"type":"TEXT","notNull":false,"unique":false},"request_fingerprint":{"type":"TEXT","notNull":false,"unique":false},"source_fingerprint":{"type":"TEXT","notNull":false,"unique":false},"source_fingerprint_nonce":{"type":"TEXT","notNull":false,"unique":false},"source_revision":{"type":"TEXT","notNull":false,"unique":false},"amount_minor":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false},"reviewed_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reviewed_by_user_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"review_evidence":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expense_allocation_revisions_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"expense_allocation_request_key","columns":["source_expense_id","request_id"],"unique":true},{"name":"expense_allocation_revision_successor","columns":["previous_revision_id"],"unique":true},{"name":"expense_allocation_revisions_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_allocation_revisions_reviewed_by_profile_id_idx","columns":["reviewed_by_profile_id"]},{"name":"expense_allocation_revisions_reviewed_by_user_id_idx","columns":["reviewed_by_user_id"]}],"version":"83425a51"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseAllocationPortion":{"name":"expenseallocationportion","className":"ExpenseAllocationPortion","qualifiedName":"@happyvertical/smrt-expenses:ExpenseAllocationPortion","collection":"expenseallocationportions","description":"Persisted command-owned ExpenseAllocationPortion evidence.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/ExpenseAllocation.ts","packageName":"@happyvertical/smrt-expenses","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"sourceExpenseId":{"type":"foreignKey","required":true,"related":"Expense","_meta":{"required":true,"onDelete":"RESTRICT"}},"revisionId":{"type":"foreignKey","required":true,"related":"ExpenseAllocationRevision","_meta":{"required":true,"onDelete":"RESTRICT"}},"portionKey":{"type":"text","required":false},"costObjectType":{"type":"text","required":false},"costObjectId":{"type":"text","required":false},"costCodeRef":{"type":"text","required":false},"amountMinor":{"type":"integer","required":false,"_meta":{}},"currency":{"type":"text","required":false}},"methods":{"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[],"returnType":"Promise<void>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expense_allocation_portions","api":{"include":[]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expense_allocation_portion_key","columns":["revisionId","portionKey"],"unique":true}],"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ExpenseAllocationPortion","collectionExportName":"ExpenseAllocationPortionCollection","validationRules":[{"field":"sourceExpenseId","rule":"required","fieldType":"foreignKey"},{"field":"revisionId","rule":"required","fieldType":"foreignKey"}],"schema":{"tableName":"expense_allocation_portions","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_allocation_portions\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"source_expense_id\\" UUID NOT NULL,\\n \\"revision_id\\" UUID NOT NULL,\\n \\"portion_key\\" TEXT,\\n \\"cost_object_type\\" TEXT,\\n \\"cost_object_id\\" TEXT,\\n \\"cost_code_ref\\" TEXT,\\n \\"amount_minor\\" INTEGER,\\n \\"currency\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"source_expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"revision_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expense_allocation_revisions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"portion_key":{"type":"TEXT","notNull":false,"unique":false},"cost_object_type":{"type":"TEXT","notNull":false,"unique":false},"cost_object_id":{"type":"TEXT","notNull":false,"unique":false},"cost_code_ref":{"type":"TEXT","notNull":false,"unique":false},"amount_minor":{"type":"INTEGER","notNull":false,"unique":false},"currency":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expense_allocation_portions_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"expense_allocation_portion_key","columns":["revision_id","portion_key"],"unique":true},{"name":"expense_allocation_portions_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_allocation_portions_source_expense_id_idx","columns":["source_expense_id"]}],"version":"6b0600b1"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseAllocationBilling":{"name":"expenseallocationbilling","className":"ExpenseAllocationBilling","qualifiedName":"@happyvertical/smrt-expenses:ExpenseAllocationBilling","collection":"expenseallocationbillings","description":"Persisted command-owned ExpenseAllocationBilling evidence.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/ExpenseAllocation.ts","packageName":"@happyvertical/smrt-expenses","fields":{"tenantId":{"type":"text","required":true,"_meta":{"sqlType":"UUID","__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":true,"autoPopulate":true,"nullable":false,"mode":"required","field":"tenantId","allowSuperAdminBypass":false}}},"sourceExpenseId":{"type":"foreignKey","required":true,"related":"Expense","_meta":{"required":true,"onDelete":"RESTRICT"}},"portionId":{"type":"foreignKey","required":true,"related":"ExpenseAllocationPortion","_meta":{"required":true,"onDelete":"RESTRICT"}},"invoiceId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:Invoice","_meta":{"nullable":true}},"draftType":{"type":"text","required":false},"draftId":{"type":"text","required":false},"invoiceLineId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-commerce:InvoiceLineItem","_meta":{"nullable":true}},"requestId":{"type":"text","required":false},"requestFingerprint":{"type":"text","required":false},"relatedReservationId":{"type":"foreignKey","required":false,"related":"ExpenseAllocationBilling","_meta":{"nullable":true,"onDelete":"RESTRICT"}},"kind":{"type":"text","required":false}},"methods":{"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[],"returnType":"Promise<void>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expense_allocation_billings","api":{"include":[]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expense_allocation_billing_request","columns":["sourceExpenseId","requestId"],"unique":true},{"name":"expense_allocation_billing_release","columns":["relatedReservationId","kind"],"unique":true}],"tenantScoped":{"mode":"required"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ExpenseAllocationBilling","collectionExportName":"ExpenseAllocationBillingCollection","validationRules":[{"field":"sourceExpenseId","rule":"required","fieldType":"foreignKey"},{"field":"portionId","rule":"required","fieldType":"foreignKey"}],"schema":{"tableName":"expense_allocation_billings","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_allocation_billings\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID NOT NULL,\\n \\"source_expense_id\\" UUID NOT NULL,\\n \\"portion_id\\" UUID NOT NULL,\\n \\"invoice_id\\" UUID,\\n \\"draft_type\\" TEXT,\\n \\"draft_id\\" TEXT,\\n \\"invoice_line_id\\" UUID,\\n \\"request_id\\" TEXT,\\n \\"request_fingerprint\\" TEXT,\\n \\"related_reservation_id\\" UUID,\\n \\"kind\\" TEXT\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":true,"unique":false},"source_expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"portion_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expense_allocation_portions","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"invoice_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"draft_type":{"type":"TEXT","notNull":false,"unique":false},"draft_id":{"type":"TEXT","notNull":false,"unique":false},"invoice_line_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"request_id":{"type":"TEXT","notNull":false,"unique":false},"request_fingerprint":{"type":"TEXT","notNull":false,"unique":false},"related_reservation_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"unique":false,"foreignKey":{"table":"expense_allocation_billings","column":"id","onDelete":"RESTRICT","onUpdate":"CASCADE"}},"kind":{"type":"TEXT","notNull":false,"unique":false}},"indexes":[{"name":"expense_allocation_billings_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true},{"name":"expense_allocation_billing_request","columns":["source_expense_id","request_id"],"unique":true},{"name":"expense_allocation_billing_release","columns":["related_reservation_id","kind"],"unique":true},{"name":"expense_allocation_billings_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_allocation_billings_portion_id_idx","columns":["portion_id"]},{"name":"expense_allocation_billings_invoice_id_idx","columns":["invoice_id"]},{"name":"expense_allocation_billings_invoice_line_id_idx","columns":["invoice_line_id"]}],"version":"432aeb1f"},"importPath":"@happyvertical/smrt-expenses"},"@happyvertical/smrt-expenses:ExpenseReceipt":{"name":"expensereceipt","className":"ExpenseReceipt","qualifiedName":"@happyvertical/smrt-expenses:ExpenseReceipt","collection":"expensereceipts","description":"A receipt file attached to an expense.","filePath":"/home/runner/work/smrt/smrt/packages/expenses/src/models/ExpenseReceipt.ts","packageName":"@happyvertical/smrt-expenses","fields":{"tenantId":{"type":"text","required":false,"_meta":{"sqlType":"UUID","nullable":true,"__tenancy":{"isTenantIdField":true,"autoFilter":true,"required":false,"autoPopulate":true,"nullable":true,"mode":"optional","field":"tenantId","allowSuperAdminBypass":false}}},"expenseId":{"type":"foreignKey","required":true,"related":"Expense","_meta":{"required":true,"onDelete":"CASCADE"}},"assetId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-assets:Asset"},"uploadedByProfileId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-profiles:Profile"},"filename":{"type":"text","required":false,"_meta":{}},"mimeType":{"type":"text","required":false,"_meta":{}},"byteCount":{"type":"integer","required":false,"_meta":{}},"contentSha256":{"type":"text","required":true,"_meta":{"required":true}}},"methods":{"save":{"name":"save","async":true,"parameters":[{"name":"options","type":"SmrtSaveOptions","optional":true}],"returnType":"Promise<this>","isStatic":false,"isPublic":true},"delete":{"name":"delete","async":true,"parameters":[{"name":"options","type":"object","optional":true,"memberTypes":["Date | string"]}],"returnType":"Promise<void>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expense_receipts","api":{"include":[]},"mcp":{"include":[]},"cli":false,"indexes":[{"name":"expense_receipts_expense_sha256_key","columns":["expenseId","contentSha256"],"unique":true},{"name":"expense_receipts_tenant_sha256_idx","columns":["tenantId","contentSha256"]}],"tenantScoped":{"mode":"optional"},"conflictColumns":["tenant_id","slug","context"]},"extends":"SmrtObject","exportName":"ExpenseReceipt","collectionExportName":"ExpenseReceiptCollection","validationRules":[{"field":"expenseId","rule":"required","fieldType":"foreignKey"},{"field":"assetId","rule":"required","fieldType":"crossPackageRef"},{"field":"contentSha256","rule":"required","fieldType":"text"}],"schema":{"tableName":"expense_receipts","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_receipts\\" (\\n \\"_sync_origin_device_id\\" TEXT,\\n \\"_sync_revision\\" INTEGER,\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"expense_id\\" UUID NOT NULL,\\n \\"asset_id\\" UUID NOT NULL,\\n \\"uploaded_by_profile_id\\" UUID,\\n \\"filename\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"byte_count\\" INTEGER,\\n \\"content_sha256\\" TEXT NOT NULL\\n);","columns":{"_sync_origin_device_id":{"type":"TEXT"},"_sync_revision":{"type":"INTEGER"},"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false,"unique":false},"expense_id":{"type":"UUID","referenceKind":"foreignKey","notNull":true,"unique":false,"foreignKey":{"table":"expenses","column":"id","onDelete":"CASCADE","onUpdate":"CASCADE"}},"asset_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"uploaded_by_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false,"unique":false},"filename":{"type":"TEXT","notNull":false,"unique":false},"mime_type":{"type":"TEXT","notNull":false,"unique":false},"byte_count":{"type":"INTEGER","notNull":false,"unique":false},"content_sha256":{"type":"TEXT","notNull":true,"unique":false}},"indexes":[{"name":"expense_receipts_slug_context_idx","columns":["tenant_id","slug","context"],"unique":true,"nullsNotDistinct":true},{"name":"expense_receipts_expense_sha256_key","columns":["expense_id","content_sha256"],"unique":true},{"name":"expense_receipts_tenant_sha256_idx","columns":["tenant_id","content_sha256"]},{"name":"expense_receipts_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"expense_receipts_asset_id_idx","columns":["asset_id"]},{"name":"expense_receipts_uploaded_by_profile_id_idx","columns":["uploaded_by_profile_id"]}],"version":"8ed688ee"},"importPath":"@happyvertical/smrt-expenses"}},"moduleType":"smrt","recipes":[{"id":"expenses.tracking","className":"ExpensesRecipe","label":"Expenses","summary":"Record what the business spends, with receipts, and review it.","synonyms":["costs","receipts","spending","reimbursements"],"models":["@happyvertical/smrt-expenses:Expense","@happyvertical/smrt-expenses:ExpenseReceipt"],"nav":[{"label":"Expenses","model":"@happyvertical/smrt-expenses:Expense","icon":"receipt","description":"Costs you have paid, with the receipt and who paid."}],"requires":[],"section":{"id":"accounting","label":"Accounting","icon":"bank","description":"Your books: accounts, journals and balances."},"options":{"@happyvertical/smrt-expenses:Expense":{"fields":{"description":{"order":1,"required":true},"amount":{"order":2},"currency":{"order":3},"incurredOn":{"label":"Date","order":4,"help":"The date on the receipt, as YYYY-MM-DD."},"category":{"order":5,"help":"One word, such as material, travel, freight or other."},"vendorId":{"label":"Vendor","order":6},"paidBy":{"order":7},"paidByProfileId":{"label":"Person who paid","order":8,"help":"Who paid out of their own pocket, when a person paid."},"paidByName":{"order":9},"reimbursable":{"order":10},"costObjectId":{"label":"Charged to","order":11,"help":"The job, project or client the cost belongs to."},"costObjectType":{"visibility":"hidden"},"activeAllocationRevisionId":{"visibility":"hidden"},"financialBreakdown":{"visibility":"hidden"},"supplierPayableJson":{"visibility":"hidden"},"refundRecipient":{"visibility":"hidden"},"reimbursedAmount":{"help":"The recorded amount paid back to the person who paid."},"refundOfId":{"visibility":"hidden"},"recordedByProfileId":{"visibility":"hidden"},"commitmentId":{"visibility":"hidden"},"commitmentLineId":{"visibility":"hidden"},"reviewedByProfileId":{"visibility":"hidden"},"duplicateOfId":{"visibility":"hidden"}}},"@happyvertical/smrt-expenses:ExpenseReceipt":{"fields":{"contentSha256":{"visibility":"hidden"},"byteCount":{"visibility":"hidden"},"uploadedByProfileId":{"visibility":"hidden"}}}},"help":{"markdown":"## Overview\\n\\nExpenses are the costs the business pays: a receipt, who paid it and what it\\nwas for. Each expense is reviewed once, and a reimbursable one is marked when\\nthe person is paid back.\\n\\n## Tasks\\n\\n### Record an expense\\n\\n1. Create a new expense and write a short **{field:description}**.\\n2. Enter the **{field:amount}** and its **{field:currency}**.\\n3. Set the **{field:incurredOn}** date from the receipt and pick a\\n **{field:category}**.\\n4. Choose who **{field:paidBy}**: the company, a person or a third party.\\n5. When a person paid, pick the **{field:paidByProfileId}** and tick\\n **{field:reimbursable}** when they are owed the money back.\\n\\n### Review it\\n\\n1. Open the expense and check the receipt attached to it.\\n2. **{field:reviewStatus}** moves from unreviewed to reviewed, or to rejected,\\n when someone reviews it. A rejected expense can be reopened.\\n","fieldRefs":["amount","category","currency","description","incurredOn","paidBy","paidByProfileId","reimbursable","reviewStatus"]},"demo":{"mode":"server","reasons":["Needs a server because its package does not build for a browser: node:crypto; and its package depends on @happyvertical/smrt-agents, @happyvertical/smrt-commerce, @happyvertical/smrt-jobs, @happyvertical/smrt-ledgers, @happyvertical/smrt-products, @happyvertical/smrt-profiles, @happyvertical/smrt-reports, @happyvertical/smrt-secrets, @happyvertical/smrt-subscriptions, @happyvertical/smrt-tenancy, @happyvertical/smrt-users, which do not build for a browser (#3615, #3616, #3617, #3624, #3625, #3626, #3627)."]}}],"smrtDependencies":["@happyvertical/smrt-assets","@happyvertical/smrt-commerce","@happyvertical/smrt-core","@happyvertical/smrt-tenancy","@happyvertical/smrt-users"],"browser":{"status":"server-only","issues":["#3615","#3616","#3617","#3624","#3625","#3626","#3627"],"reason":"node:crypto","via":["@happyvertical/smrt-agents","@happyvertical/smrt-commerce","@happyvertical/smrt-jobs","@happyvertical/smrt-ledgers","@happyvertical/smrt-products","@happyvertical/smrt-profiles","@happyvertical/smrt-reports","@happyvertical/smrt-secrets","@happyvertical/smrt-subscriptions","@happyvertical/smrt-tenancy","@happyvertical/smrt-users"]},"stampsConstructors":true}`));
12
+ //#endregion
13
+ //#region src/permissions.ts
14
+ var EXPENSE_ALLOCATION_PERMISSION_DEFINITIONS = [{
15
+ slug: "expenses.read",
16
+ name: "Read allocated expense portions",
17
+ description: "Read bounded expense allocation portions through current host target authority; does not expose original expense or receipt records.",
18
+ category: "accounting"
19
+ }];
20
+ registerPermissionDefinitions(EXPENSE_ALLOCATION_PERMISSION_DEFINITIONS);
21
+ //#endregion
22
+ //#region src/models/ExpenseAllocation.ts
23
+ var __defProp$2 = Object.defineProperty;
24
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
25
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value
30
+ }) : obj[key] = value;
31
+ var __decorateClass$2 = (decorators, target, key, kind) => {
32
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
33
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
34
+ if (kind && result) __defProp$2(target, key, result);
35
+ return result;
36
+ };
37
+ var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
38
+ var ExpenseAllocationRevision = class extends SmrtObject {
39
+ kind = "allocation";
40
+ /** Immutable bounded source facts retained even after unbilled withdrawal/correction. */
41
+ sourceCostObjectType = "";
42
+ sourceCostObjectId = "";
43
+ incurredOn = "";
44
+ paidBy = "company";
45
+ reimbursable = false;
46
+ hasSupplierTax = null;
47
+ basisEqualsGross = false;
48
+ tenantId = "";
49
+ sourceExpenseId = "";
50
+ previousRevisionId = null;
51
+ requestId = "";
52
+ requestFingerprint = "";
53
+ sourceFingerprint = "";
54
+ /** Secret to bounded portion readers; prevents low-entropy source-total guessing from its digest. */
55
+ sourceFingerprintNonce = "";
56
+ sourceRevision = "";
57
+ amountMinor = 0;
58
+ currency = "";
59
+ reviewedByProfileId = "";
60
+ reviewedByUserId = "";
61
+ reviewEvidence = "";
62
+ constructor(options = {}) {
63
+ super(options);
64
+ for (const key of [
65
+ "kind",
66
+ "sourceCostObjectType",
67
+ "sourceCostObjectId",
68
+ "incurredOn",
69
+ "paidBy",
70
+ "reimbursable",
71
+ "hasSupplierTax",
72
+ "basisEqualsGross",
73
+ "tenantId",
74
+ "sourceExpenseId",
75
+ "previousRevisionId",
76
+ "requestId",
77
+ "requestFingerprint",
78
+ "sourceFingerprint",
79
+ "sourceFingerprintNonce",
80
+ "sourceRevision",
81
+ "amountMinor",
82
+ "currency",
83
+ "reviewedByProfileId",
84
+ "reviewedByUserId",
85
+ "reviewEvidence"
86
+ ]) if (options[key] !== void 0) Object.assign(this, { [key]: options[key] });
87
+ }
88
+ /** Refuse all writes except command-owned inserts, including same-id adoption. */
89
+ async save(options) {
90
+ if (!allocationWrites.has(this) || !this.tenantId || this.isPersisted) throw new Error("Expense allocation evidence is command-owned and immutable");
91
+ this.requireInsertOnSave();
92
+ return super.save(options);
93
+ }
94
+ /** Evidence cannot be erased, including superseded revisions. */
95
+ async delete() {
96
+ throw new Error("Expense allocation evidence cannot be deleted");
97
+ }
98
+ };
99
+ __publicField$2(ExpenseAllocationRevision, "__smrtPackage__", "@happyvertical/smrt-expenses");
100
+ __decorateClass$2([field({
101
+ type: "boolean",
102
+ nullable: true
103
+ })], ExpenseAllocationRevision.prototype, "hasSupplierTax", 2);
104
+ __decorateClass$2([tenantId()], ExpenseAllocationRevision.prototype, "tenantId", 2);
105
+ __decorateClass$2([foreignKey("Expense", {
106
+ required: true,
107
+ onDelete: "RESTRICT"
108
+ })], ExpenseAllocationRevision.prototype, "sourceExpenseId", 2);
109
+ __decorateClass$2([foreignKey("ExpenseAllocationRevision", {
110
+ nullable: true,
111
+ onDelete: "RESTRICT"
112
+ })], ExpenseAllocationRevision.prototype, "previousRevisionId", 2);
113
+ __decorateClass$2([field({ type: "integer" })], ExpenseAllocationRevision.prototype, "amountMinor", 2);
114
+ __decorateClass$2([crossPackageRef("@happyvertical/smrt-profiles:Profile", { required: true })], ExpenseAllocationRevision.prototype, "reviewedByProfileId", 2);
115
+ __decorateClass$2([crossPackageRef("@happyvertical/smrt-users:User", { required: true })], ExpenseAllocationRevision.prototype, "reviewedByUserId", 2);
116
+ ExpenseAllocationRevision = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
117
+ tableName: "expense_allocation_revisions",
118
+ api: { include: [] },
119
+ mcp: { include: [] },
120
+ cli: false,
121
+ indexes: [{
122
+ name: "expense_allocation_request_key",
123
+ columns: ["sourceExpenseId", "requestId"],
124
+ unique: true
125
+ }, {
126
+ name: "expense_allocation_revision_successor",
127
+ columns: ["previousRevisionId"],
128
+ unique: true
129
+ }]
130
+ })], ExpenseAllocationRevision);
131
+ var ExpenseAllocationPortion = class extends SmrtObject {
132
+ tenantId = "";
133
+ sourceExpenseId = "";
134
+ revisionId = "";
135
+ portionKey = "";
136
+ costObjectType = "";
137
+ costObjectId = "";
138
+ costCodeRef = "";
139
+ amountMinor = 0;
140
+ currency = "";
141
+ constructor(options = {}) {
142
+ super(options);
143
+ for (const key of [
144
+ "tenantId",
145
+ "sourceExpenseId",
146
+ "revisionId",
147
+ "portionKey",
148
+ "costObjectType",
149
+ "costObjectId",
150
+ "costCodeRef",
151
+ "amountMinor",
152
+ "currency"
153
+ ]) if (options[key] !== void 0) Object.assign(this, { [key]: options[key] });
154
+ }
155
+ /** Refuse all writes except command-owned inserts, including same-id adoption. */
156
+ async save(options) {
157
+ if (!allocationWrites.has(this) || !this.tenantId || this.isPersisted) throw new Error("Expense allocation evidence is command-owned and immutable");
158
+ this.requireInsertOnSave();
159
+ return super.save(options);
160
+ }
161
+ /** Evidence cannot be erased, including superseded revisions. */
162
+ async delete() {
163
+ throw new Error("Expense allocation evidence cannot be deleted");
164
+ }
165
+ };
166
+ __publicField$2(ExpenseAllocationPortion, "__smrtPackage__", "@happyvertical/smrt-expenses");
167
+ __decorateClass$2([tenantId()], ExpenseAllocationPortion.prototype, "tenantId", 2);
168
+ __decorateClass$2([foreignKey("Expense", {
169
+ required: true,
170
+ onDelete: "RESTRICT"
171
+ })], ExpenseAllocationPortion.prototype, "sourceExpenseId", 2);
172
+ __decorateClass$2([foreignKey("ExpenseAllocationRevision", {
173
+ required: true,
174
+ onDelete: "RESTRICT"
175
+ })], ExpenseAllocationPortion.prototype, "revisionId", 2);
176
+ __decorateClass$2([field({ type: "integer" })], ExpenseAllocationPortion.prototype, "amountMinor", 2);
177
+ ExpenseAllocationPortion = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
178
+ tableName: "expense_allocation_portions",
179
+ api: { include: [] },
180
+ mcp: { include: [] },
181
+ cli: false,
182
+ indexes: [{
183
+ name: "expense_allocation_portion_key",
184
+ columns: ["revisionId", "portionKey"],
185
+ unique: true
186
+ }]
187
+ })], ExpenseAllocationPortion);
188
+ var ExpenseAllocationBilling = class extends SmrtObject {
189
+ tenantId = "";
190
+ sourceExpenseId = "";
191
+ portionId = "";
192
+ invoiceId = null;
193
+ draftType = "";
194
+ draftId = "";
195
+ invoiceLineId = null;
196
+ requestId = "";
197
+ requestFingerprint = "";
198
+ relatedReservationId = null;
199
+ kind = "reserve";
200
+ constructor(options = {}) {
201
+ super(options);
202
+ for (const key of [
203
+ "tenantId",
204
+ "sourceExpenseId",
205
+ "portionId",
206
+ "invoiceId",
207
+ "draftType",
208
+ "draftId",
209
+ "invoiceLineId",
210
+ "requestId",
211
+ "requestFingerprint",
212
+ "relatedReservationId",
213
+ "kind"
214
+ ]) if (options[key] !== void 0) Object.assign(this, { [key]: options[key] });
215
+ }
216
+ /** Refuse all writes except command-owned inserts, including same-id adoption. */
217
+ async save(options) {
218
+ if (!allocationWrites.has(this) || !this.tenantId || this.isPersisted) throw new Error("Expense allocation evidence is command-owned and immutable");
219
+ this.requireInsertOnSave();
220
+ return super.save(options);
221
+ }
222
+ /** Evidence cannot be erased, including superseded revisions. */
223
+ async delete() {
224
+ throw new Error("Expense allocation evidence cannot be deleted");
225
+ }
226
+ };
227
+ __publicField$2(ExpenseAllocationBilling, "__smrtPackage__", "@happyvertical/smrt-expenses");
228
+ __decorateClass$2([tenantId()], ExpenseAllocationBilling.prototype, "tenantId", 2);
229
+ __decorateClass$2([foreignKey("Expense", {
230
+ required: true,
231
+ onDelete: "RESTRICT"
232
+ })], ExpenseAllocationBilling.prototype, "sourceExpenseId", 2);
233
+ __decorateClass$2([foreignKey("ExpenseAllocationPortion", {
234
+ required: true,
235
+ onDelete: "RESTRICT"
236
+ })], ExpenseAllocationBilling.prototype, "portionId", 2);
237
+ __decorateClass$2([crossPackageRef("@happyvertical/smrt-commerce:Invoice", { nullable: true })], ExpenseAllocationBilling.prototype, "invoiceId", 2);
238
+ __decorateClass$2([crossPackageRef("@happyvertical/smrt-commerce:InvoiceLineItem", { nullable: true })], ExpenseAllocationBilling.prototype, "invoiceLineId", 2);
239
+ __decorateClass$2([foreignKey("ExpenseAllocationBilling", {
240
+ nullable: true,
241
+ onDelete: "RESTRICT"
242
+ })], ExpenseAllocationBilling.prototype, "relatedReservationId", 2);
243
+ ExpenseAllocationBilling = __decorateClass$2([TenantScoped({ mode: "required" }), smrt({
244
+ tableName: "expense_allocation_billings",
245
+ api: { include: [] },
246
+ mcp: { include: [] },
247
+ cli: false,
248
+ indexes: [{
249
+ name: "expense_allocation_billing_request",
250
+ columns: ["sourceExpenseId", "requestId"],
251
+ unique: true
252
+ }, {
253
+ name: "expense_allocation_billing_release",
254
+ columns: ["relatedReservationId", "kind"],
255
+ unique: true
256
+ }]
257
+ })], ExpenseAllocationBilling);
258
+ //#endregion
259
+ //#region src/expense-allocation.ts
260
+ var __defProp$1 = Object.defineProperty;
261
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
262
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
263
+ enumerable: true,
264
+ configurable: true,
265
+ writable: true,
266
+ value
267
+ }) : obj[key] = value;
268
+ var __decorateClass$1 = (decorators, target2, key, kind) => {
269
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target2, key) : target2;
270
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target2, key, result) : decorator(result)) || result;
271
+ if (kind && result) __defProp$1(target2, key, result);
272
+ return result;
273
+ };
274
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
275
+ var Revisions = class extends SmrtCollection {};
276
+ __publicField$1(Revisions, "__smrtPackage__", "@happyvertical/smrt-expenses");
277
+ __publicField$1(Revisions, "_itemClass", ExpenseAllocationRevision);
278
+ Revisions = __decorateClass$1([smrt({
279
+ api: { include: [] },
280
+ mcp: { include: [] },
281
+ cli: false
282
+ })], Revisions);
283
+ var Portions = class extends SmrtCollection {};
284
+ __publicField$1(Portions, "__smrtPackage__", "@happyvertical/smrt-expenses");
285
+ __publicField$1(Portions, "_itemClass", ExpenseAllocationPortion);
286
+ Portions = __decorateClass$1([smrt({
287
+ api: { include: [] },
288
+ mcp: { include: [] },
289
+ cli: false
290
+ })], Portions);
291
+ var Billings = class extends SmrtCollection {};
292
+ __publicField$1(Billings, "__smrtPackage__", "@happyvertical/smrt-expenses");
293
+ __publicField$1(Billings, "_itemClass", ExpenseAllocationBilling);
294
+ Billings = __decorateClass$1([smrt({
295
+ api: { include: [] },
296
+ mcp: { include: [] },
297
+ cli: false
298
+ })], Billings);
299
+ function invalid(message) {
300
+ throw new ExpenseError("EXPENSE_INVALID", `Expense allocation: ${message}`);
301
+ }
302
+ function text(value) {
303
+ if (typeof value !== "string" || !value.trim() || value !== value.trim() || value.length > 1e3) invalid("bounded nonempty identifiers/evidence required.");
304
+ }
305
+ function digest(value) {
306
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex");
307
+ }
308
+ function target(row) {
309
+ if (!row || Object.keys(row).some((key) => ![
310
+ "portionKey",
311
+ "costObjectType",
312
+ "costObjectId",
313
+ "costCodeRef",
314
+ "amountMinor"
315
+ ].includes(key))) invalid("unknown portion field.");
316
+ for (const value of [
317
+ row.portionKey,
318
+ row.costObjectType,
319
+ row.costObjectId,
320
+ row.costCodeRef
321
+ ]) text(value);
322
+ assertQualifiedClassName("ExpenseAllocation", row.portionKey, row.costObjectType);
323
+ if (!Number.isSafeInteger(row.amountMinor) || row.amountMinor <= 0) invalid("portions require positive safe integer minor units.");
324
+ return {
325
+ portionKey: row.portionKey,
326
+ costObjectType: row.costObjectType,
327
+ costObjectId: row.costObjectId,
328
+ costCodeRef: row.costCodeRef,
329
+ amountMinor: row.amountMinor
330
+ };
331
+ }
332
+ async function portionsFor(source, revisionId) {
333
+ return (await Portions.create({ db: source.db })).list({
334
+ where: {
335
+ tenantId: source.tenantId,
336
+ revisionId
337
+ },
338
+ limit: 1e3,
339
+ orderBy: ["portion_key ASC"]
340
+ });
341
+ }
342
+ async function sourceFor(db, id, tenant) {
343
+ const { ExpenseCollection } = await Promise.resolve().then(() => ExpenseCollection_exports);
344
+ const source = await (await ExpenseCollection.create({ db })).get({
345
+ id,
346
+ tenantId: tenant
347
+ }, { cache: false });
348
+ if (!source || source.tenantId !== tenant) invalid("owned source required.");
349
+ return source;
350
+ }
351
+ async function lock(source) {
352
+ if (isPostgresDatabase(source.db)) await source.db.query(`SELECT id FROM "${source.tableName}" WHERE id = ? AND tenant_id = ? FOR UPDATE`, source.id, source.tenantId);
353
+ await source.loadFromId();
354
+ }
355
+ async function authorize(source, principal, authority, targets) {
356
+ if (typeof authority?.authorizeTarget !== "function") invalid("host target authorization is required.");
357
+ for (const item of targets) {
358
+ if (!ObjectRegistry.getClassByQualifiedName(item.costObjectType)) invalid("unknown cost-object type.");
359
+ const object = await (await ObjectRegistry.getCollection(item.costObjectType, { db: source.db })).get({ id: item.costObjectId }, { cache: false });
360
+ if (!object || object.tenantId !== principal.tenantId) invalid("target must exist in the active tenant.");
361
+ await authority.authorizeTarget(principal, Object.freeze({ ...item }), source.db);
362
+ }
363
+ }
364
+ async function identity(source, revision, row) {
365
+ if (!row.id || !source.id || !revision.id) invalid("persisted allocation identities required.");
366
+ const { ExpenseCollection } = await Promise.resolve().then(() => ExpenseCollection_exports);
367
+ const hasRefunds = (await (await ExpenseCollection.create({ db: source.db })).list({
368
+ where: {
369
+ refundOfId: source.id,
370
+ tenantId: source.tenantId
371
+ },
372
+ select: ["id"],
373
+ limit: 1
374
+ })).length > 0;
375
+ return {
376
+ portionId: row.id,
377
+ sourceExpenseId: source.id,
378
+ revisionId: revision.id,
379
+ sourceFingerprint: revision.sourceFingerprint,
380
+ incurredOn: revision.incurredOn,
381
+ paidBy: revision.paidBy,
382
+ reimbursable: revision.reimbursable,
383
+ hasSupplierTax: revision.hasSupplierTax,
384
+ hasRefunds,
385
+ costBasisMinor: !hasRefunds && revision.basisEqualsGross ? row.amountMinor : null,
386
+ currency: revision.currency,
387
+ active: source.activeAllocationRevisionId === revision.id,
388
+ portionKey: row.portionKey,
389
+ costObjectType: row.costObjectType,
390
+ costObjectId: row.costObjectId,
391
+ costCodeRef: row.costCodeRef,
392
+ amountMinor: row.amountMinor
393
+ };
394
+ }
395
+ async function activeReservations(source) {
396
+ return (await source.db.query(`SELECT r.id, r.portion_id FROM expense_allocation_billings r
397
+ WHERE r.source_expense_id = ? AND r.tenant_id = ? AND r.kind = 'reserve'
398
+ AND NOT EXISTS (SELECT 1 FROM expense_allocation_billings x
399
+ WHERE x.related_reservation_id = r.id AND x.tenant_id = r.tenant_id AND x.kind = 'release')`, source.id, source.tenantId)).rows.map((row) => ({
400
+ id: row.id,
401
+ portionId: row.portion_id
402
+ }));
403
+ }
404
+ async function latestRevision(source) {
405
+ const result = await source.db.query(`SELECT r.id FROM expense_allocation_revisions r
406
+ WHERE r.source_expense_id = ? AND r.tenant_id = ? AND NOT EXISTS
407
+ (SELECT 1 FROM expense_allocation_revisions n WHERE n.previous_revision_id = r.id AND n.source_expense_id = r.source_expense_id AND n.tenant_id = r.tenant_id)
408
+ LIMIT 2`, source.id, source.tenantId);
409
+ if (result.rows.length > 1) invalid("allocation audit chain is ambiguous.");
410
+ const id = result.rows[0]?.id;
411
+ return id ? (await Revisions.create({ db: source.db })).get({
412
+ id,
413
+ sourceExpenseId: source.id,
414
+ tenantId: source.tenantId
415
+ }, { cache: false }) : null;
416
+ }
417
+ async function reviewExpenseAllocation(db, input, authority) {
418
+ if (!input || Object.keys(input).some((key) => ![
419
+ "sourceExpenseId",
420
+ "expectedSourceRevision",
421
+ "requestId",
422
+ "portions",
423
+ "reviewEvidence"
424
+ ].includes(key))) invalid("unknown input field.");
425
+ text(input.sourceExpenseId);
426
+ text(input.requestId);
427
+ text(input.reviewEvidence);
428
+ if (instantMs(input.expectedSourceRevision) === null || !Array.isArray(input.portions) || input.portions.length < 1 || input.portions.length > 1e3) invalid("source revision and 1–1000 portions required.");
429
+ const proposed = input.portions.map(target).sort((a, b) => a.portionKey < b.portionKey ? -1 : a.portionKey > b.portionKey ? 1 : 0);
430
+ if (new Set(proposed.map((row) => row.portionKey)).size !== proposed.length) invalid("duplicate portion key.");
431
+ const fingerprint = digest({
432
+ sourceExpenseId: input.sourceExpenseId,
433
+ expectedSourceRevision: new Date(input.expectedSourceRevision).toISOString(),
434
+ portions: proposed,
435
+ reviewEvidence: input.reviewEvidence
436
+ });
437
+ const principal = await authorizeExpenseAction("expenses", "recordReview", { db }, { allowSuperAdminBypass: false });
438
+ return withActionPrincipal(principal, async () => {
439
+ return (await sourceFor(db, input.sourceExpenseId, principal.tenantId)).withTransaction(async (source) => {
440
+ await lock(source);
441
+ if (typeof authority?.authorizeSource !== "function") invalid("host original-source authorization is required.");
442
+ await authority.authorizeSource(principal, source.id, source.db, { action: "reviewAllocation" });
443
+ const prior = await (await Revisions.create({ db: source.db })).get({
444
+ sourceExpenseId: source.id,
445
+ tenantId: principal.tenantId,
446
+ requestId: input.requestId
447
+ }, { cache: false });
448
+ if (prior) {
449
+ if (prior.kind !== "allocation" || prior.requestFingerprint !== fingerprint) invalid("idempotency key payload conflict.");
450
+ const children2 = await portionsFor(source, prior.id);
451
+ await authorize(source, principal, authority, children2);
452
+ return {
453
+ revision: prior,
454
+ portions: await Promise.all(children2.map((row) => identity(source, prior, row)))
455
+ };
456
+ }
457
+ if (instantMs(source.updated_at) !== instantMs(input.expectedSourceRevision)) invalid("stale source revision.");
458
+ if (source.reviewStatus !== "reviewed" || source.duplicateOfId || source.amount <= 0 || source.refundOfId) invalid("positive reviewed original required.");
459
+ if (proposed.reduce((sum, row) => sum + BigInt(row.amountMinor), 0n) !== BigInt(source.amount)) invalid("portions must exactly equal source amount; no remainder.");
460
+ const old = source.activeAllocationRevisionId ? await portionsFor(source, source.activeAllocationRevisionId) : [];
461
+ await authorize(source, principal, authority, [...old, ...proposed]);
462
+ if ((await activeReservations(source)).length) invalid("release draft reservations before correction; issued portions require explicit credit/reversal.");
463
+ await source.claimRevision(input.expectedSourceRevision);
464
+ const previous = await latestRevision(source);
465
+ if (source.activeAllocationRevisionId ? previous?.id !== source.activeAllocationRevisionId || previous.kind !== "allocation" : previous && previous.kind !== "withdrawal") invalid("allocation audit chain disagrees with its active head.");
466
+ const sourceNonce = previous?.sourceFingerprintNonce || randomBytes(32).toString("hex");
467
+ const revision = await insertAllocation(new ExpenseAllocationRevision({
468
+ db: source.db,
469
+ tenantId: principal.tenantId,
470
+ sourceExpenseId: source.id,
471
+ previousRevisionId: previous?.id ?? null,
472
+ kind: "allocation",
473
+ sourceCostObjectType: source.costObjectType,
474
+ sourceCostObjectId: source.costObjectId,
475
+ incurredOn: source.incurredOn,
476
+ paidBy: source.paidBy,
477
+ reimbursable: source.reimbursable,
478
+ hasSupplierTax: source.getFinancialBreakdown()?.taxes.some((tax) => tax.amount !== 0) ?? null,
479
+ basisEqualsGross: source.getCostBasis()?.costBasis === source.amount,
480
+ requestId: input.requestId,
481
+ requestFingerprint: fingerprint,
482
+ sourceFingerprintNonce: sourceNonce,
483
+ sourceFingerprint: digest({
484
+ nonce: sourceNonce,
485
+ id: source.id,
486
+ amount: source.amount,
487
+ currency: source.currency,
488
+ incurredOn: source.incurredOn,
489
+ financialBreakdown: source.financialBreakdown,
490
+ vendorId: source.vendorId,
491
+ paidBy: source.paidBy,
492
+ reimbursable: source.reimbursable,
493
+ category: source.category,
494
+ costObjectType: source.costObjectType,
495
+ costObjectId: source.costObjectId,
496
+ commitmentId: source.commitmentId,
497
+ commitmentLineId: source.commitmentLineId,
498
+ paidByProfileId: source.paidByProfileId,
499
+ paidByName: source.paidByName
500
+ }),
501
+ sourceRevision: new Date(input.expectedSourceRevision).toISOString(),
502
+ amountMinor: source.amount,
503
+ currency: source.currency,
504
+ reviewedByProfileId: principal.profileId,
505
+ reviewedByUserId: principal.userId,
506
+ reviewEvidence: input.reviewEvidence
507
+ }));
508
+ const children = [];
509
+ for (const item of proposed) children.push(await insertAllocation(new ExpenseAllocationPortion({
510
+ db: source.db,
511
+ ...item,
512
+ tenantId: principal.tenantId,
513
+ sourceExpenseId: source.id,
514
+ revisionId: revision.id,
515
+ currency: source.currency
516
+ })));
517
+ source.activeAllocationRevisionId = revision.id;
518
+ allocationHeads.set(source, revision.id);
519
+ try {
520
+ await source.save();
521
+ } finally {
522
+ allocationHeads.delete(source);
523
+ }
524
+ return {
525
+ revision,
526
+ portions: await Promise.all(children.map((row) => identity(source, revision, row)))
527
+ };
528
+ });
529
+ });
530
+ }
531
+ async function authorizeUnsplit(source, principal, revision, authority) {
532
+ if (typeof authority.authorizeUnsplitTarget !== "function") invalid("host restored-unsplit target authorization is required.");
533
+ const costObjectType = revision.sourceCostObjectType;
534
+ const costObjectId = revision.sourceCostObjectId;
535
+ if (Boolean(costObjectType) !== Boolean(costObjectId)) invalid("restored unsplit destination is malformed.");
536
+ if (costObjectType) {
537
+ if (!ObjectRegistry.getClassByQualifiedName(costObjectType)) invalid("restored unsplit type is unknown.");
538
+ const object = await (await ObjectRegistry.getCollection(costObjectType, { db: source.db })).get({ id: costObjectId }, { cache: false });
539
+ if (!object || object.tenantId !== principal.tenantId) invalid("restored unsplit destination must exist in the active tenant.");
540
+ }
541
+ await authority.authorizeUnsplitTarget(principal, Object.freeze({
542
+ sourceExpenseId: source.id,
543
+ costObjectType,
544
+ costObjectId
545
+ }), source.db);
546
+ }
547
+ async function withdrawExpenseAllocation(db, input, authority) {
548
+ if (!input || Object.keys(input).some((key) => ![
549
+ "sourceExpenseId",
550
+ "expectedActiveRevisionId",
551
+ "expectedSourceRevision",
552
+ "requestId",
553
+ "reviewEvidence"
554
+ ].includes(key))) invalid("unknown withdrawal input field.");
555
+ text(input.sourceExpenseId);
556
+ text(input.expectedActiveRevisionId);
557
+ text(input.requestId);
558
+ text(input.reviewEvidence);
559
+ if (instantMs(input.expectedSourceRevision) === null) invalid("withdrawal source revision is required.");
560
+ const fingerprint = digest({
561
+ action: "withdrawAllocation",
562
+ sourceExpenseId: input.sourceExpenseId,
563
+ expectedActiveRevisionId: input.expectedActiveRevisionId,
564
+ expectedSourceRevision: new Date(input.expectedSourceRevision).toISOString(),
565
+ reviewEvidence: input.reviewEvidence
566
+ });
567
+ const principal = await billingPrincipal(db);
568
+ return withActionPrincipal(principal, async () => {
569
+ return (await sourceFor(db, input.sourceExpenseId, principal.tenantId)).withTransaction(async (source) => {
570
+ await lock(source);
571
+ if (typeof authority?.authorizeSource !== "function") invalid("host original-source authorization is required.");
572
+ await authority.authorizeSource(principal, source.id, source.db, { action: "withdrawAllocation" });
573
+ const revisions = await Revisions.create({ db: source.db });
574
+ const replay = await revisions.get({
575
+ sourceExpenseId: source.id,
576
+ tenantId: principal.tenantId,
577
+ requestId: input.requestId
578
+ }, { cache: false });
579
+ if (replay && (replay.kind !== "withdrawal" || replay.requestFingerprint !== fingerprint)) invalid("withdrawal idempotency key payload conflict.");
580
+ const targetRevisionId = replay?.previousRevisionId ?? input.expectedActiveRevisionId;
581
+ const previous = await revisions.get({
582
+ id: targetRevisionId,
583
+ sourceExpenseId: source.id,
584
+ tenantId: principal.tenantId
585
+ }, { cache: false });
586
+ if (!previous || previous.kind !== "allocation") invalid("withdrawal requires the original reviewed allocation revision.");
587
+ const old = await portionsFor(source, previous.id);
588
+ await authorize(source, principal, authority, old);
589
+ await authorizeUnsplit(source, principal, previous, authority);
590
+ if (replay) return {
591
+ revision: replay,
592
+ withdrawnRevisionId: previous.id
593
+ };
594
+ if (source.activeAllocationRevisionId !== input.expectedActiveRevisionId || instantMs(source.updated_at) !== instantMs(input.expectedSourceRevision)) invalid("stale active allocation or source revision.");
595
+ if ((await latestRevision(source))?.id !== previous.id) invalid("withdrawal audit chain is not current.");
596
+ if ((await activeReservations(source)).length) invalid("reserved, bound or issued portions cannot be withdrawn; release authorized draft claims explicitly.");
597
+ await source.claimRevision(input.expectedSourceRevision);
598
+ const revision = await insertAllocation(new ExpenseAllocationRevision({
599
+ db: source.db,
600
+ tenantId: principal.tenantId,
601
+ sourceExpenseId: source.id,
602
+ previousRevisionId: previous.id,
603
+ kind: "withdrawal",
604
+ requestId: input.requestId,
605
+ requestFingerprint: fingerprint,
606
+ sourceFingerprint: previous.sourceFingerprint,
607
+ sourceFingerprintNonce: previous.sourceFingerprintNonce,
608
+ sourceRevision: new Date(input.expectedSourceRevision).toISOString(),
609
+ amountMinor: previous.amountMinor,
610
+ currency: previous.currency,
611
+ sourceCostObjectType: previous.sourceCostObjectType,
612
+ sourceCostObjectId: previous.sourceCostObjectId,
613
+ incurredOn: previous.incurredOn,
614
+ paidBy: previous.paidBy,
615
+ reimbursable: previous.reimbursable,
616
+ hasSupplierTax: previous.hasSupplierTax,
617
+ basisEqualsGross: previous.basisEqualsGross,
618
+ reviewedByProfileId: principal.profileId,
619
+ reviewedByUserId: principal.userId,
620
+ reviewEvidence: input.reviewEvidence
621
+ }));
622
+ source.activeAllocationRevisionId = null;
623
+ allocationHeads.set(source, null);
624
+ try {
625
+ await source.save();
626
+ } finally {
627
+ allocationHeads.delete(source);
628
+ }
629
+ return {
630
+ revision,
631
+ withdrawnRevisionId: previous.id
632
+ };
633
+ });
634
+ });
635
+ }
636
+ async function resolveExpenseAllocationPortion(db, portionId, authority) {
637
+ text(portionId);
638
+ const principal = await authorizeExpenseAction("expenses", "get", { db }, { allowSuperAdminBypass: false });
639
+ return withActionPrincipal(principal, async () => {
640
+ const row = await (await Portions.create({ db })).get({
641
+ id: portionId,
642
+ tenantId: principal.tenantId
643
+ }, { cache: false });
644
+ if (!row) invalid("owned portion required.");
645
+ const source = await sourceFor(db, row.sourceExpenseId, principal.tenantId);
646
+ if (source.activeAllocationRevisionId === row.revisionId && (source.reviewStatus !== "reviewed" || source.duplicateOfId)) invalid("source review is no longer usable.");
647
+ const revision = await (await Revisions.create({ db })).get({
648
+ id: row.revisionId,
649
+ tenantId: principal.tenantId
650
+ }, { cache: false });
651
+ if (!revision || revision.sourceExpenseId !== source.id) invalid("owned revision required.");
652
+ await authorize(source, principal, authority, [row]);
653
+ return identity(source, revision, row);
654
+ });
655
+ }
656
+ async function allocationTargetAmount(source, filter) {
657
+ if (!source.activeAllocationRevisionId) {
658
+ if (Object.keys(filter).some((key) => key.startsWith("costCodeRef"))) return null;
659
+ const { ExpenseCollection } = await Promise.resolve().then(() => ExpenseCollection_exports);
660
+ return await (await ExpenseCollection.create({ db: source.db })).get({
661
+ ...filter,
662
+ id: source.id,
663
+ tenantId: source.tenantId
664
+ }, { cache: false }) ? source.amount : null;
665
+ }
666
+ const rows = await (await Portions.create({ db: source.db })).list({
667
+ where: {
668
+ ...filter,
669
+ revisionId: source.activeAllocationRevisionId,
670
+ tenantId: source.tenantId
671
+ },
672
+ limit: 1e3
673
+ });
674
+ if (!rows.length) return null;
675
+ const total = rows.reduce((sum, row) => sum + BigInt(row.amountMinor), 0n);
676
+ if (total > BigInt(Number.MAX_SAFE_INTEGER)) invalid("target sum exceeds safe range.");
677
+ return Number(total);
678
+ }
679
+ var EXPENSE_ALLOCATION_INVOICE_SOURCE = "billable:@happyvertical/smrt-expenses:ExpenseAllocationPortion";
680
+ async function reservationFor(db, id, tenantId) {
681
+ const reservation = await (await Billings.create({ db })).get({
682
+ id,
683
+ tenantId
684
+ }, { cache: false });
685
+ if (!reservation || reservation.kind !== "reserve" || reservation.tenantId !== tenantId) invalid("owned reservation required.");
686
+ return reservation;
687
+ }
688
+ async function bindingFor(source, reservationId) {
689
+ return (await Billings.create({ db: source.db })).get({
690
+ sourceExpenseId: source.id,
691
+ relatedReservationId: reservationId,
692
+ kind: "bind",
693
+ tenantId: source.tenantId
694
+ }, { cache: false });
695
+ }
696
+ async function billingPrincipal(db) {
697
+ return authorizeExpenseAction("expenses", "recordReview", { db }, { allowSuperAdminBypass: false });
698
+ }
699
+ async function billingPortion(source, id, principal, authority, action) {
700
+ const portion = await (await Portions.create({ db: source.db })).get({
701
+ id,
702
+ sourceExpenseId: source.id,
703
+ tenantId: principal.tenantId
704
+ }, { cache: false });
705
+ if (!portion) invalid("owned source portion required.");
706
+ await authorize(source, principal, authority, [portion]);
707
+ if (typeof authority.authorizeSource !== "function") invalid("host source authority required.");
708
+ await authority.authorizeSource(principal, source.id, source.db, {
709
+ action,
710
+ portion: {
711
+ portionId: portion.id,
712
+ portionKey: portion.portionKey,
713
+ costObjectType: portion.costObjectType,
714
+ costObjectId: portion.costObjectId,
715
+ costCodeRef: portion.costCodeRef,
716
+ amountMinor: portion.amountMinor
717
+ }
718
+ });
719
+ return portion;
720
+ }
721
+ async function reserveExpenseAllocationPortion(db, input, authority) {
722
+ text(input.portionId);
723
+ text(input.requestId);
724
+ if (Object.keys(input).some((key) => ![
725
+ "portionId",
726
+ "requestId",
727
+ "draftType",
728
+ "draftId",
729
+ "invoiceId",
730
+ "invoiceLineId"
731
+ ].includes(key))) invalid("unknown reservation field.");
732
+ const principal = await billingPrincipal(db);
733
+ return withActionPrincipal(principal, async () => {
734
+ const row = await (await Portions.create({ db })).get({
735
+ id: input.portionId,
736
+ tenantId: principal.tenantId
737
+ }, { cache: false });
738
+ if (!row) invalid("owned portion required.");
739
+ return (await sourceFor(db, row.sourceExpenseId, principal.tenantId)).withTransaction(async (source) => {
740
+ await lock(source);
741
+ const portion = await billingPortion(source, row.id, principal, authority, "reserveAllocation");
742
+ const invoiceBound = Boolean(input.invoiceId || input.invoiceLineId);
743
+ if (invoiceBound) {
744
+ text(input.invoiceId);
745
+ text(input.invoiceLineId);
746
+ if (typeof authority.authorizeInvoice !== "function") invalid("host invoice authority required.");
747
+ await authority.authorizeInvoice(principal, portion, input.invoiceId, source.db);
748
+ } else {
749
+ text(input.draftType);
750
+ text(input.draftId);
751
+ if (typeof authority.authorizeDraft !== "function") invalid("host draft authority required.");
752
+ await authority.authorizeDraft(principal, portion, {
753
+ type: input.draftType,
754
+ id: input.draftId
755
+ }, source.db);
756
+ }
757
+ const fingerprint = digest({
758
+ portionId: input.portionId,
759
+ requestId: input.requestId,
760
+ draftType: input.draftType ?? null,
761
+ draftId: input.draftId ?? null,
762
+ invoiceId: input.invoiceId ?? null,
763
+ invoiceLineId: input.invoiceLineId ?? null
764
+ });
765
+ const prior = await (await Billings.create({ db: source.db })).get({
766
+ sourceExpenseId: source.id,
767
+ tenantId: principal.tenantId,
768
+ requestId: input.requestId
769
+ }, { cache: false });
770
+ if (prior) {
771
+ if (prior.requestFingerprint !== fingerprint || prior.kind !== "reserve") invalid("reservation idempotency conflict.");
772
+ return prior;
773
+ }
774
+ if ((await source.getRefundPosition()).refundIds.length) invalid("refunded source needs explicit billing reversal policy.");
775
+ if (source.activeAllocationRevisionId !== portion.revisionId) invalid("superseded portion cannot be reserved.");
776
+ if ((await activeReservations(source)).some((item) => item.portionId === portion.id)) invalid("portion capacity already reserved.");
777
+ if (!source.updated_at) invalid("source revision required.");
778
+ await source.claimRevision(source.updated_at);
779
+ const reservation = await insertAllocation(new ExpenseAllocationBilling({
780
+ db: source.db,
781
+ tenantId: principal.tenantId,
782
+ sourceExpenseId: source.id,
783
+ portionId: portion.id,
784
+ requestId: input.requestId,
785
+ requestFingerprint: fingerprint,
786
+ kind: "reserve",
787
+ draftType: input.draftType ?? "",
788
+ draftId: input.draftId ?? ""
789
+ }));
790
+ if (invoiceBound) await bindReservation(source, reservation, portion, {
791
+ invoiceId: input.invoiceId,
792
+ invoiceLineId: input.invoiceLineId,
793
+ requestId: `${input.requestId}:bind`
794
+ }, principal, authority);
795
+ return reservation;
796
+ });
797
+ });
798
+ }
799
+ async function bindReservation(source, reservation, portion, input, principal, authority) {
800
+ if (typeof authority.authorizeInvoice !== "function") invalid("host invoice authority required.");
801
+ await authority.authorizeInvoice(principal, portion, input.invoiceId, source.db);
802
+ const billings = await Billings.create({ db: source.db });
803
+ const fingerprint = digest({
804
+ reservationId: reservation.id,
805
+ ...input
806
+ });
807
+ const previous = await bindingFor(source, reservation.id);
808
+ if (previous) {
809
+ if (previous.requestId !== input.requestId || previous.requestFingerprint !== fingerprint) invalid("reservation already bound or payload changed.");
810
+ return previous;
811
+ }
812
+ if ((await source.getRefundPosition()).refundIds.length) invalid("refunded source needs explicit billing reversal policy.");
813
+ if (!(await activeReservations(source)).some((item) => item.id === reservation.id)) invalid("released reservation cannot bind.");
814
+ if (await billings.get({
815
+ sourceExpenseId: source.id,
816
+ tenantId: principal.tenantId,
817
+ requestId: input.requestId
818
+ }, { cache: false })) invalid("request identity already used.");
819
+ const invoice = await (await InvoiceCollection.create({ db: source.db })).get({
820
+ id: input.invoiceId,
821
+ tenantId: principal.tenantId
822
+ }, { cache: false });
823
+ const line = await (await InvoiceLineItemCollection.create({ db: source.db })).get({
824
+ id: input.invoiceLineId,
825
+ tenantId: principal.tenantId
826
+ }, { cache: false });
827
+ if (!invoice || invoice.tenantId !== principal.tenantId || invoice.status !== "draft" || invoice.currency !== portion.currency || !line || line.tenantId !== principal.tenantId || line.invoiceId !== invoice.id || line.sourceType !== "billable:@happyvertical/smrt-expenses:ExpenseAllocationPortion" || line.sourceId !== portion.id || !invoice.updated_at || !source.updated_at) invalid("owned same-currency draft and canonical source line required.");
828
+ await invoice.claimRevision(invoice.updated_at);
829
+ await source.claimRevision(source.updated_at);
830
+ const binding = await insertAllocation(new ExpenseAllocationBilling({
831
+ db: source.db,
832
+ tenantId: principal.tenantId,
833
+ sourceExpenseId: source.id,
834
+ portionId: portion.id,
835
+ invoiceId: input.invoiceId,
836
+ invoiceLineId: input.invoiceLineId,
837
+ requestId: input.requestId,
838
+ requestFingerprint: fingerprint,
839
+ kind: "bind",
840
+ relatedReservationId: reservation.id
841
+ }));
842
+ await bindInvoiceLineSource({
843
+ db: source.db,
844
+ tenantId: principal.tenantId,
845
+ lineId: input.invoiceLineId,
846
+ reservationId: reservation.id
847
+ });
848
+ return binding;
849
+ }
850
+ async function bindExpenseAllocationPortion(db, reservationId, input, authority) {
851
+ text(reservationId);
852
+ text(input.invoiceId);
853
+ text(input.invoiceLineId);
854
+ text(input.requestId);
855
+ const principal = await billingPrincipal(db);
856
+ return withActionPrincipal(principal, async () => {
857
+ const reservation = await reservationFor(db, reservationId, principal.tenantId);
858
+ return (await sourceFor(db, reservation.sourceExpenseId, principal.tenantId)).withTransaction(async (source) => {
859
+ await lock(source);
860
+ const portion = await billingPortion(source, reservation.portionId, principal, authority, "bindAllocation");
861
+ return bindReservation(source, reservation, portion, input, principal, authority);
862
+ });
863
+ });
864
+ }
865
+ async function releaseExpenseAllocationPortion(db, reservationId, requestId, authority) {
866
+ text(reservationId);
867
+ text(requestId);
868
+ const principal = await billingPrincipal(db);
869
+ return withActionPrincipal(principal, async () => {
870
+ const reservation = await reservationFor(db, reservationId, principal.tenantId);
871
+ return (await sourceFor(db, reservation.sourceExpenseId, principal.tenantId)).withTransaction(async (source) => {
872
+ await lock(source);
873
+ const portion = await billingPortion(source, reservation.portionId, principal, authority, "releaseAllocation");
874
+ const binding = await bindingFor(source, reservation.id);
875
+ if (binding?.invoiceId) {
876
+ if (typeof authority.authorizeInvoice !== "function") invalid("host invoice authority required.");
877
+ await authority.authorizeInvoice(principal, portion, binding.invoiceId, source.db);
878
+ } else {
879
+ if (typeof authority.authorizeDraft !== "function") invalid("host draft authority required.");
880
+ await authority.authorizeDraft(principal, portion, {
881
+ type: reservation.draftType,
882
+ id: reservation.draftId
883
+ }, source.db);
884
+ }
885
+ const billings = await Billings.create({ db: source.db });
886
+ const fingerprint = digest({
887
+ reservationId,
888
+ requestId
889
+ });
890
+ const prior = await billings.get({
891
+ sourceExpenseId: source.id,
892
+ tenantId: principal.tenantId,
893
+ requestId
894
+ }, { cache: false });
895
+ if (prior) {
896
+ if (prior.requestFingerprint !== fingerprint || prior.kind !== "release") invalid("release idempotency conflict.");
897
+ return prior;
898
+ }
899
+ if (!(await activeReservations(source)).some((item) => item.id === reservationId)) invalid("reservation already released.");
900
+ if (binding?.invoiceId) {
901
+ const invoice = await (await InvoiceCollection.create({ db: source.db })).get({
902
+ id: binding.invoiceId,
903
+ tenantId: principal.tenantId
904
+ }, { cache: false });
905
+ if (!invoice || invoice.status !== "draft" || !invoice.updated_at) invalid("issued reservation cannot be released; use explicit credit/reversal.");
906
+ await invoice.claimRevision(invoice.updated_at);
907
+ }
908
+ if (!source.updated_at) invalid("source revision required.");
909
+ await source.claimRevision(source.updated_at);
910
+ const released = await insertAllocation(new ExpenseAllocationBilling({
911
+ db: source.db,
912
+ tenantId: principal.tenantId,
913
+ sourceExpenseId: source.id,
914
+ portionId: reservation.portionId,
915
+ invoiceId: binding?.invoiceId ?? null,
916
+ invoiceLineId: binding?.invoiceLineId ?? null,
917
+ requestId,
918
+ requestFingerprint: fingerprint,
919
+ kind: "release",
920
+ relatedReservationId: reservationId,
921
+ draftType: reservation.draftType,
922
+ draftId: reservation.draftId
923
+ }));
924
+ if (binding?.invoiceLineId) await unbindInvoiceLineSource({
925
+ db: source.db,
926
+ tenantId: principal.tenantId,
927
+ lineId: binding.invoiceLineId,
928
+ reservationId
929
+ });
930
+ return released;
931
+ });
932
+ });
933
+ }
934
+ registerInvoiceSourceBinding(EXPENSE_ALLOCATION_INVOICE_SOURCE, async ({ executor, invoice, line, reservationId, phase }) => {
935
+ if (!invoice.tenantId || line.tenantId !== invoice.tenantId) invalid("binding tenant mismatch.");
936
+ const reservation = await reservationFor(executor, reservationId, invoice.tenantId);
937
+ const source = await sourceFor(executor, reservation.sourceExpenseId, invoice.tenantId);
938
+ const binding = await bindingFor(source, reservationId);
939
+ if (!binding || binding.invoiceId !== invoice.id || binding.invoiceLineId !== line.id || line.sourceId !== reservation.portionId || line.sourceType !== "billable:@happyvertical/smrt-expenses:ExpenseAllocationPortion") invalid("binding does not identify this exact invoice line.");
940
+ const active = (await activeReservations(source)).some((item) => item.id === reservationId);
941
+ if (phase === "release" ? active : !active) invalid("binding reservation state is invalid.");
942
+ });
943
+ async function getExpenseAllocationPortionPosition(db, portionId, authority) {
944
+ const identity2 = await resolveExpenseAllocationPortion(db, portionId, authority);
945
+ const principal = await authorizeExpenseAction("expenses", "get", { db }, { allowSuperAdminBypass: false });
946
+ return withActionPrincipal(principal, async () => {
947
+ const source = await sourceFor(db, identity2.sourceExpenseId, principal.tenantId);
948
+ identity2.active = source.activeAllocationRevisionId === identity2.revisionId;
949
+ const active = (await activeReservations(source)).find((row) => row.portionId === portionId);
950
+ if (!active) return {
951
+ identity: identity2,
952
+ available: identity2.active,
953
+ reservation: null
954
+ };
955
+ const reservation = await reservationFor(db, active.id, principal.tenantId);
956
+ const binding = await bindingFor(source, active.id);
957
+ const invoice = binding?.invoiceId ? await (await InvoiceCollection.create({ db })).get({
958
+ id: binding.invoiceId,
959
+ tenantId: principal.tenantId
960
+ }, { cache: false }) : null;
961
+ return {
962
+ identity: identity2,
963
+ available: false,
964
+ reservation: {
965
+ reservationId: reservation.id,
966
+ draftType: reservation.draftType,
967
+ draftId: reservation.draftId,
968
+ invoiceId: binding?.invoiceId ?? null,
969
+ invoiceLineId: binding?.invoiceLineId ?? null,
970
+ invoiceStatus: invoice?.status ?? null,
971
+ issued: Boolean(binding && (!invoice || invoice.status !== "draft"))
972
+ }
973
+ };
974
+ });
975
+ }
9
976
  //#endregion
10
977
  //#region src/collections/ExpenseCollection.ts
11
978
  var ExpenseCollection_exports = /* @__PURE__ */ __exportAll({ ExpenseCollection: () => ExpenseCollection });
@@ -88,19 +1055,19 @@ var ExpenseCollection = class extends SmrtCollection {
88
1055
  return source.getRefundPosition();
89
1056
  }
90
1057
  async record(options) {
91
- const { recordExpenseAction } = await import("./chunks/expense-actions-DiIhw0xO.js");
1058
+ const { recordExpenseAction } = await import("./chunks/expense-actions-DszuHacx.js");
92
1059
  return recordExpenseAction(this, options);
93
1060
  }
94
1061
  async attachReceipt(options) {
95
- const { attachExpenseReceiptAction } = await import("./chunks/expense-actions-DiIhw0xO.js");
1062
+ const { attachExpenseReceiptAction } = await import("./chunks/expense-actions-DszuHacx.js");
96
1063
  return attachExpenseReceiptAction(this, options);
97
1064
  }
98
1065
  async recordReview(options) {
99
- const { reviewExpenseAction } = await import("./chunks/expense-actions-DiIhw0xO.js");
1066
+ const { reviewExpenseAction } = await import("./chunks/expense-actions-DszuHacx.js");
100
1067
  return reviewExpenseAction(this, options);
101
1068
  }
102
1069
  async recordReimbursement(options) {
103
- const { markExpenseReimbursedAction } = await import("./chunks/expense-actions-DiIhw0xO.js");
1070
+ const { markExpenseReimbursedAction } = await import("./chunks/expense-actions-DszuHacx.js");
104
1071
  return markExpenseReimbursedAction(this, options);
105
1072
  }
106
1073
  /**
@@ -135,23 +1102,35 @@ var ExpenseCollection = class extends SmrtCollection {
135
1102
  async totalsByCurrency(options = {}) {
136
1103
  const statuses = options.reviewStatuses ?? ["reviewed"];
137
1104
  if (statuses.length === 0) return {};
138
- const rows = await this.list({
139
- where: {
140
- ...options.where ?? {},
141
- reviewStatus: [...statuses],
142
- duplicateOfId: null
143
- },
144
- select: [...AMOUNT_FIELDS]
145
- });
1105
+ const where = { ...options.where ?? {} };
1106
+ const targetFilter = {};
1107
+ for (const key of Object.keys(where)) if (/^cost(ObjectType|ObjectId|CodeRef)/.test(key)) {
1108
+ targetFilter[key] = where[key];
1109
+ delete where[key];
1110
+ }
1111
+ const rows = await this.list({ where: {
1112
+ ...where,
1113
+ reviewStatus: [...statuses],
1114
+ duplicateOfId: null
1115
+ } });
146
1116
  const totals = {};
147
1117
  const seen = /* @__PURE__ */ new Set();
148
1118
  for (const row of rows) {
149
- if (seen.has(row.id)) continue;
1119
+ if (!row.id || seen.has(row.id)) continue;
150
1120
  seen.add(row.id);
151
- addTo(totals, row.currency, toMinorUnits(row.amount));
1121
+ const amount = Object.keys(targetFilter).length ? await allocationTargetAmount(row, targetFilter) : toMinorUnits(row.amount);
1122
+ if (amount !== null) addTo(totals, row.currency, amount);
152
1123
  }
153
1124
  return totals;
154
1125
  }
1126
+ /** Review a complete allocation through ambient reviewer and mandatory host authority. */
1127
+ async reviewAllocation(input, authority) {
1128
+ return reviewExpenseAllocation(this.db, input, authority);
1129
+ }
1130
+ /** Audit withdrawal of entirely unreserved portions before correcting the original source. */
1131
+ async withdrawAllocation(input, authority) {
1132
+ return withdrawExpenseAllocation(this.db, input, authority);
1133
+ }
155
1134
  /**
156
1135
  * Stored supplier-tax facts for reporting. Only reviewed non-duplicate
157
1136
  * expenses are returned; callers must retain a null breakdown as unknown.
@@ -356,6 +1335,7 @@ var ExpensesRecipe = class extends SmrtRecipe {
356
1335
  help: "The job, project or client the cost belongs to."
357
1336
  },
358
1337
  costObjectType: { visibility: "hidden" },
1338
+ activeAllocationRevisionId: { visibility: "hidden" },
359
1339
  financialBreakdown: { visibility: "hidden" },
360
1340
  supplierPayableJson: { visibility: "hidden" },
361
1341
  refundRecipient: { visibility: "hidden" },
@@ -375,6 +1355,6 @@ var ExpensesRecipe = class extends SmrtRecipe {
375
1355
  };
376
1356
  };
377
1357
  //#endregion
378
- export { DuplicateReceiptError, EXPENSE_PAYERS, EXPENSE_REVIEW_STATUSES, Expense, ExpenseActionError, ExpenseCollection, ExpenseError, ExpenseReceipt, ExpenseReceiptCollection, ExpensesRecipe, SUGGESTED_EXPENSE_CATEGORIES, computeContentSha256, ExpenseCollection_exports as t };
1358
+ export { DuplicateReceiptError, EXPENSE_ALLOCATION_INVOICE_SOURCE, EXPENSE_ALLOCATION_PERMISSION_DEFINITIONS, EXPENSE_PAYERS, EXPENSE_REVIEW_STATUSES, Expense, ExpenseActionError, ExpenseAllocationBilling, ExpenseAllocationPortion, ExpenseAllocationRevision, ExpenseCollection, ExpenseError, ExpenseReceipt, ExpenseReceiptCollection, ExpensesRecipe, SUGGESTED_EXPENSE_CATEGORIES, bindExpenseAllocationPortion, computeContentSha256, getExpenseAllocationPortionPosition, releaseExpenseAllocationPortion, reserveExpenseAllocationPortion, resolveExpenseAllocationPortion, reviewExpenseAllocation, ExpenseCollection_exports as t, withdrawExpenseAllocation };
379
1359
 
380
1360
  //# sourceMappingURL=index.js.map