@happyvertical/smrt-expenses 0.56.0 → 0.56.1

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.
package/dist/index.js CHANGED
@@ -1,41 +1,34 @@
1
+ import { n as ExpenseReceipt, r as __exportAll, t as ExpenseReceiptCollection } from "./chunks/ExpenseReceiptCollection-y1SlmnBS.js";
2
+ import { t as ExpenseActionError } from "./chunks/principal-_1CdcvvW.js";
1
3
  import { DuplicateReceiptError, EXPENSE_PAYERS, EXPENSE_REVIEW_STATUSES, ExpenseError, SUGGESTED_EXPENSE_CATEGORIES } from "./types.js";
2
- import { a as identityConflict, c as pinNaturalKey, i as effectiveTenant, n as assertMinorUnits, o as normalizeSha256, r as computeContentSha256, s as normalizeTenantId, t as Expense } from "./chunks/Expense-i8NeN2D3.js";
3
- import { ObjectRegistry, SmrtCollection, SmrtJunction, SmrtObject, classifyDatabaseError, crossPackageRef, field, foreignKey, isUniqueViolationError, smrt } from "@happyvertical/smrt-core";
4
+ import { r as computeContentSha256, t as Expense } from "./chunks/Expense-7afLcUTn.js";
5
+ import { ObjectRegistry, SmrtCollection, SmrtRecipe, method, smrt } from "@happyvertical/smrt-core";
4
6
  import { ContractLineItemCollection, PurchaseContractCollection } from "@happyvertical/smrt-commerce";
5
- import { TenantScoped, tenantId } from "@happyvertical/smrt-tenancy";
6
- import { AssetCollection } from "@happyvertical/smrt-assets";
7
- //#region \0rolldown/runtime.js
8
- var __defProp$3 = Object.defineProperty;
9
- var __exportAll = (all, no_symbols) => {
10
- let target = {};
11
- for (var name in all) __defProp$3(target, name, {
12
- get: all[name],
13
- enumerable: true
14
- });
15
- if (!no_symbols) __defProp$3(target, Symbol.toStringTag, { value: "Module" });
16
- return target;
17
- };
18
- //#endregion
19
7
  //#region src/__smrt-register__.ts
20
- ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-expenses","packageVersion":"0.56.0","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":{"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},"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":[]},"mcp":{"include":[]},"cli":false,"tableName":"expenses"},"extends":"SmrtCollection","extendsTypeArg":"Expense","exportName":"ExpenseCollection","collectionExportName":"ExpenseCollectionCollection","schema":{"tableName":"expenses","ddl":"CREATE TABLE IF NOT EXISTS \\"expenses\\" (\\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 \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\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_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT\\n);","columns":{"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},"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"}},"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_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":"287668fa"},"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","schema":{"tableName":"expense_receipts","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_receipts\\" (\\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":{"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":"5aa77571"},"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":{}},"paidByName":{"type":"text","required":false,"_meta":{}},"refundOfId":{"type":"foreignKey","required":false,"related":"Expense","_meta":{"onDelete":"RESTRICT"}},"incurredOn":{"type":"text","required":false,"_meta":{}},"recordedAt":{"type":"datetime","required":false},"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":{}},"reimbursedAt":{"type":"datetime","required":false},"reimbursementReference":{"type":"text","required":false,"_meta":{}}},"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},"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":[],"returnType":"Promise<this>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"expenses","api":{"include":[]},"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 \\"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 \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\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_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT\\n);","columns":{"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},"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"}},"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_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":"287668fa"},"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":[],"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 \\"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":{"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":"5aa77571"},"importPath":"@happyvertical/smrt-expenses"}},"moduleType":"smrt","smrtDependencies":["@happyvertical/smrt-assets","@happyvertical/smrt-commerce","@happyvertical/smrt-core","@happyvertical/smrt-tenancy"],"browser":{"status":"server-only","issues":["#3615","#3624","#3626","#3627"],"reason":"node:crypto","via":["@happyvertical/smrt-commerce","@happyvertical/smrt-jobs","@happyvertical/smrt-ledgers","@happyvertical/smrt-subscriptions","@happyvertical/smrt-tenancy"]},"stampsConstructors":true}`));
8
+ ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-expenses","packageVersion":"0.56.1","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":{"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","schema":{"tableName":"expenses","ddl":"CREATE TABLE IF NOT EXISTS \\"expenses\\" (\\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 \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\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_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT\\n);","columns":{"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},"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"}},"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_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":"287668fa"},"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","schema":{"tableName":"expense_receipts","ddl":"CREATE TABLE IF NOT EXISTS \\"expense_receipts\\" (\\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":{"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":"5aa77571"},"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":{}},"paidByName":{"type":"text","required":false,"_meta":{}},"refundOfId":{"type":"foreignKey","required":false,"related":"Expense","_meta":{"onDelete":"RESTRICT"}},"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":{}},"reimbursedAt":{"type":"datetime","required":false,"_meta":{}},"reimbursementReference":{"type":"text","required":false,"_meta":{}}},"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},"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":[],"returnType":"Promise<this>","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 \\"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 \\"paid_by_name\\" TEXT,\\n \\"refund_of_id\\" UUID,\\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_at\\" TIMESTAMP,\\n \\"reimbursement_reference\\" TEXT\\n);","columns":{"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},"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"}},"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_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":"287668fa"},"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":[],"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 \\"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":{"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":"5aa77571"},"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"},"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}`));
21
9
  //#endregion
22
10
  //#region src/collections/ExpenseCollection.ts
23
11
  var ExpenseCollection_exports = /* @__PURE__ */ __exportAll({ ExpenseCollection: () => ExpenseCollection });
24
- var __defProp$2 = Object.defineProperty;
25
- var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
26
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, {
12
+ var __defProp = Object.defineProperty;
13
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
27
15
  enumerable: true,
28
16
  configurable: true,
29
17
  writable: true,
30
18
  value
31
19
  }) : obj[key] = value;
32
- var __decorateClass$2 = (decorators, target, key, kind) => {
33
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
20
+ var __decorateClass = (decorators, target, key, kind) => {
21
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
34
22
  for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
35
- if (kind && result) __defProp$2(target, key, result);
23
+ if (kind && result) __defProp(target, key, result);
36
24
  return result;
37
25
  };
38
- var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
26
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
27
+ function isCalendarDate(value) {
28
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
29
+ const date = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
30
+ return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
31
+ }
39
32
  var AMOUNT_FIELDS = [
40
33
  "id",
41
34
  "amount",
@@ -61,6 +54,22 @@ function addTo(totals, currency, amount) {
61
54
  };
62
55
  }
63
56
  var ExpenseCollection = class extends SmrtCollection {
57
+ async record(options) {
58
+ const { recordExpenseAction } = await import("./chunks/expense-actions-Ce5CBaBE.js");
59
+ return recordExpenseAction(this, options);
60
+ }
61
+ async attachReceipt(options) {
62
+ const { attachExpenseReceiptAction } = await import("./chunks/expense-actions-Ce5CBaBE.js");
63
+ return attachExpenseReceiptAction(this, options);
64
+ }
65
+ async recordReview(options) {
66
+ const { reviewExpenseAction } = await import("./chunks/expense-actions-Ce5CBaBE.js");
67
+ return reviewExpenseAction(this, options);
68
+ }
69
+ async recordReimbursement(options) {
70
+ const { markExpenseReimbursedAction } = await import("./chunks/expense-actions-Ce5CBaBE.js");
71
+ return markExpenseReimbursedAction(this, options);
72
+ }
64
73
  /**
65
74
  * Expenses charged to one cost object, oldest incurred first.
66
75
  *
@@ -111,6 +120,36 @@ var ExpenseCollection = class extends SmrtCollection {
111
120
  return totals;
112
121
  }
113
122
  /**
123
+ * Stored supplier-tax facts for reporting. Only reviewed non-duplicate
124
+ * expenses are returned; callers must retain a null breakdown as unknown.
125
+ */
126
+ async listReviewedTaxFacts(options) {
127
+ if (!options.tenantId?.trim() || !isCalendarDate(options.from) || !isCalendarDate(options.to) || options.from > options.to || options.currency !== void 0 && !options.currency.trim()) throw new ExpenseError("EXPENSE_INVALID", "Tax facts require a tenant, an ordered calendar-date range, and a nonempty currency when supplied.");
128
+ return (await this.list({
129
+ where: {
130
+ tenantId: options.tenantId,
131
+ reviewStatus: "reviewed",
132
+ duplicateOfId: null,
133
+ "incurredOn >=": options.from,
134
+ "incurredOn <=": options.to,
135
+ ...options.currency ? { currency: options.currency } : {}
136
+ },
137
+ orderBy: ["incurred_on ASC", "id ASC"]
138
+ })).map((expense) => {
139
+ if (!expense.id || expense.tenantId !== options.tenantId) throw new ExpenseError("EXPENSE_INVALID", "Tenant tax-fact query returned a row without its requested tenant identity.");
140
+ return {
141
+ id: expense.id,
142
+ expenseId: expense.id,
143
+ tenantId: expense.tenantId,
144
+ incurredOn: expense.incurredOn,
145
+ currency: expense.currency,
146
+ commitmentId: expense.commitmentId,
147
+ commitmentLineId: expense.commitmentLineId,
148
+ financialBreakdown: expense.getFinancialBreakdown()
149
+ };
150
+ });
151
+ }
152
+ /**
114
153
  * What remains open on a commitment — a commerce `Contract`, normally a
115
154
  * `PurchaseOrder` — or on one of its lines.
116
155
  *
@@ -179,298 +218,127 @@ var ExpenseCollection = class extends SmrtCollection {
179
218
  return position;
180
219
  }
181
220
  };
182
- __publicField$2(ExpenseCollection, "__smrtPackage__", "@happyvertical/smrt-expenses");
183
- __publicField$2(ExpenseCollection, "_itemClass", Expense);
184
- ExpenseCollection = __decorateClass$2([smrt({
185
- api: { include: [] },
221
+ __publicField(ExpenseCollection, "__smrtPackage__", "@happyvertical/smrt-expenses");
222
+ __publicField(ExpenseCollection, "_itemClass", Expense);
223
+ __decorateClass([method({
224
+ httpMethod: "POST",
225
+ path: "record",
226
+ effect: "write",
227
+ idempotent: false,
228
+ openWorld: false,
229
+ description: "Record a new expense for review (service-backed)."
230
+ })], ExpenseCollection.prototype, "record", 1);
231
+ __decorateClass([method({
232
+ httpMethod: "POST",
233
+ path: "attachReceipt",
234
+ effect: "write",
235
+ idempotent: false,
236
+ openWorld: false,
237
+ description: "Attach a receipt file to an expense (service-backed)."
238
+ })], ExpenseCollection.prototype, "attachReceipt", 1);
239
+ __decorateClass([method({
240
+ httpMethod: "POST",
241
+ path: "recordReview",
242
+ effect: "write",
243
+ idempotent: false,
244
+ openWorld: false,
245
+ description: "Approve or reject an expense with a reason (service-backed)."
246
+ })], ExpenseCollection.prototype, "recordReview", 1);
247
+ __decorateClass([method({
248
+ httpMethod: "POST",
249
+ path: "recordReimbursement",
250
+ effect: "write",
251
+ idempotent: false,
252
+ openWorld: false,
253
+ description: "Record that an expense was reimbursed (service-backed)."
254
+ })], ExpenseCollection.prototype, "recordReimbursement", 1);
255
+ ExpenseCollection = __decorateClass([smrt({
256
+ api: { include: [
257
+ "record",
258
+ "attachReceipt",
259
+ "recordReview",
260
+ "recordReimbursement"
261
+ ] },
186
262
  mcp: { include: [] },
187
263
  cli: false
188
264
  })], ExpenseCollection);
189
265
  //#endregion
190
- //#region src/models/ExpenseReceipt.ts
191
- var __defProp$1 = Object.defineProperty;
192
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
193
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, {
194
- enumerable: true,
195
- configurable: true,
196
- writable: true,
197
- value
198
- }) : obj[key] = value;
199
- var __decorateClass$1 = (decorators, target, key, kind) => {
200
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
201
- for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
202
- if (kind && result) __defProp$1(target, key, result);
203
- return result;
204
- };
205
- var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
206
- var RECEIPT_FILE_KEY = "expense_receipts_expense_sha256_key";
207
- var RECEIPT_FILE_COLUMNS = /* @__PURE__ */ new Set(["expense_id", "content_sha256"]);
208
- function violatedColumns(error) {
209
- const details = error?.details;
210
- return (typeof details?.fieldName === "string" ? details.fieldName : "").split(",").map((column) => column.trim()).filter(Boolean);
211
- }
212
- var IMMUTABLE = [
213
- ["tenantId", "tenant_id"],
214
- ["expenseId", "expense_id"],
215
- ["assetId", "asset_id"],
216
- ["contentSha256", "content_sha256"]
217
- ];
218
- var ExpenseReceipt = class extends SmrtObject {
219
- tenantId = null;
220
- expenseId = "";
221
- assetId = "";
222
- uploadedByProfileId = null;
223
- filename = "";
224
- mimeType = "";
225
- byteCount = 0;
226
- contentSha256 = "";
227
- constructor(options = {}) {
228
- super(options);
229
- if (options.tenantId !== void 0) this.tenantId = options.tenantId;
230
- if (options.expenseId !== void 0) this.expenseId = options.expenseId;
231
- if (options.assetId !== void 0) this.assetId = options.assetId;
232
- if (options.uploadedByProfileId !== void 0) this.uploadedByProfileId = options.uploadedByProfileId;
233
- if (options.filename !== void 0) this.filename = options.filename;
234
- if (options.mimeType !== void 0) this.mimeType = options.mimeType;
235
- if (options.byteCount !== void 0) this.byteCount = options.byteCount;
236
- if (options.contentSha256 !== void 0) this.contentSha256 = options.contentSha256;
237
- }
238
- /**
239
- * Validate the link, refuse a second copy of the same file on the same
240
- * expense, then persist. Never touches the expense.
241
- *
242
- * @throws {DuplicateReceiptError} when the expense already has this file.
243
- * @throws {ExpenseError} `EXPENSE_RECEIPT_INVALID` for a bad hash, a
244
- * missing or foreign-tenant expense or asset, or a change to an
245
- * immutable column.
246
- */
247
- async save() {
248
- const label = this.id || "<new>";
249
- this.tenantId = normalizeTenantId(this.tenantId);
250
- this.contentSha256 = normalizeSha256("ExpenseReceipt", label, this.contentSha256);
251
- assertMinorUnits("ExpenseReceipt", label, "byteCount", this.byteCount, "EXPENSE_RECEIPT_INVALID");
252
- if (!this.expenseId || !this.assetId) throw new ExpenseError("EXPENSE_RECEIPT_INVALID", `ExpenseReceipt ${label}: expenseId and assetId are required.`);
253
- const persisted = this.id ? await this.db.get(this.tableName, { id: this.id }) ?? null : null;
254
- const inserting = await pinNaturalKey("ExpenseReceipt", this, persisted);
255
- if (persisted) {
256
- const self = this;
257
- for (const [fieldName, column] of IMMUTABLE) {
258
- const value = fieldName === "tenantId" ? effectiveTenant(this.tenantId) : self[fieldName];
259
- if (String(persisted[column] ?? "") !== String(value ?? "")) throw new ExpenseError("EXPENSE_RECEIPT_INVALID", `ExpenseReceipt ${this.id}: ${fieldName} cannot change; attach a new receipt instead.`);
260
- }
261
- } else {
262
- await this.assertLinkTargets(label);
263
- await this.assertNotDuplicate();
264
- }
265
- try {
266
- return await super.save();
267
- } catch (error) {
268
- if (inserting && isUniqueViolationError(error)) throw await this.classifyInsertConflict(error);
269
- throw error;
270
- }
271
- }
272
- /**
273
- * A refused INSERT is either the same file racing onto the same expense,
274
- * or a natural-key collision with another receipt.
275
- *
276
- * Classified from the error first, by the violated key's name or columns
277
- * (no other unique key on this table uses `expense_id` or
278
- * `content_sha256`). That matters inside a caller's transaction,
279
- * which the violation has already aborted on PostgreSQL, so nothing could
280
- * be re-read there. Only an adapter that reports neither falls back to a
281
- * re-read; if that re-read fails too, the original error is kept rather
282
- * than guessed at.
283
- */
284
- async classifyInsertConflict(error) {
285
- const info = classifyDatabaseError(error);
286
- const columns = violatedColumns(error);
287
- if (info.constraint === RECEIPT_FILE_KEY || info.driverMessages.some((text) => text.includes(RECEIPT_FILE_KEY)) || columns.some((column) => RECEIPT_FILE_COLUMNS.has(column))) return new DuplicateReceiptError(this.expenseId, this.contentSha256, { cause: error });
288
- if (info.constraint || columns.length > 0) return identityConflict("ExpenseReceipt", this, error);
289
- try {
290
- await this.assertNotDuplicate();
291
- } catch (duplicate) {
292
- if (duplicate instanceof DuplicateReceiptError) return new DuplicateReceiptError(this.expenseId, this.contentSha256, { cause: error });
293
- return error;
294
- }
295
- return identityConflict("ExpenseReceipt", this, error);
296
- }
297
- async assertLinkTargets(label) {
298
- const { ExpenseCollection } = await Promise.resolve().then(() => ExpenseCollection_exports);
299
- const expense = await (await ExpenseCollection.create({ db: this.db })).get({ id: this.expenseId });
300
- if (!expense) throw new ExpenseError("EXPENSE_RECEIPT_INVALID", `ExpenseReceipt ${label}: expense ${this.expenseId} was not found.`);
301
- if (this.tenantId === null || this.tenantId === void 0) this.tenantId = expense.tenantId ?? null;
302
- if ((this.tenantId ?? null) !== (expense.tenantId ?? null)) throw new ExpenseError("EXPENSE_RECEIPT_INVALID", `ExpenseReceipt ${label}: a receipt belongs to its expense's tenant.`);
303
- const asset = await (await AssetCollection.create({ db: this.db })).get({ id: this.assetId });
304
- const assetTenant = asset?.tenantId ?? null;
305
- if (!asset || assetTenant !== null && assetTenant !== this.tenantId) throw new ExpenseError("EXPENSE_RECEIPT_INVALID", `ExpenseReceipt ${label}: asset ${this.assetId} was not found in the expense's tenant.`);
306
- if (!this.filename) this.filename = asset.name ?? "";
307
- if (!this.mimeType) this.mimeType = asset.mimeType ?? "";
308
- }
309
- async assertNotDuplicate() {
310
- const { ExpenseReceiptCollection } = await Promise.resolve().then(() => ExpenseReceiptCollection_exports);
311
- if ((await (await ExpenseReceiptCollection.create({ db: this.db })).list({
312
- where: {
313
- expenseId: this.expenseId,
314
- contentSha256: this.contentSha256
266
+ //#region src/recipes.ts
267
+ var ExpensesRecipe = class extends SmrtRecipe {
268
+ static id = "expenses.tracking";
269
+ static help = "./tracking.recipe.md";
270
+ static label = "Expenses";
271
+ static summary = "Record what the business spends, with receipts, and review it.";
272
+ static synonyms = [
273
+ "costs",
274
+ "receipts",
275
+ "spending",
276
+ "reimbursements"
277
+ ];
278
+ static section = {
279
+ id: "accounting",
280
+ label: "Accounting",
281
+ icon: "bank",
282
+ description: "Your books: accounts, journals and balances."
283
+ };
284
+ static models = [Expense, ExpenseReceipt];
285
+ static nav = [{
286
+ label: "Expenses",
287
+ model: Expense,
288
+ icon: "receipt",
289
+ description: "Costs you have paid, with the receipt and who paid."
290
+ }];
291
+ static options = {
292
+ Expense: { fields: {
293
+ description: {
294
+ order: 1,
295
+ required: true
315
296
  },
316
- limit: 1
317
- })).length > 0) throw new DuplicateReceiptError(this.expenseId, this.contentSha256);
318
- }
319
- };
320
- __publicField$1(ExpenseReceipt, "__smrtPackage__", "@happyvertical/smrt-expenses");
321
- __decorateClass$1([tenantId({ nullable: true })], ExpenseReceipt.prototype, "tenantId", 2);
322
- __decorateClass$1([foreignKey(Expense, {
323
- required: true,
324
- onDelete: "CASCADE"
325
- })], ExpenseReceipt.prototype, "expenseId", 2);
326
- __decorateClass$1([crossPackageRef("@happyvertical/smrt-assets:Asset", { required: true })], ExpenseReceipt.prototype, "assetId", 2);
327
- __decorateClass$1([crossPackageRef("@happyvertical/smrt-profiles:Profile")], ExpenseReceipt.prototype, "uploadedByProfileId", 2);
328
- __decorateClass$1([field({ type: "text" })], ExpenseReceipt.prototype, "filename", 2);
329
- __decorateClass$1([field({ type: "text" })], ExpenseReceipt.prototype, "mimeType", 2);
330
- __decorateClass$1([field({ type: "integer" })], ExpenseReceipt.prototype, "byteCount", 2);
331
- __decorateClass$1([field({
332
- type: "text",
333
- required: true
334
- })], ExpenseReceipt.prototype, "contentSha256", 2);
335
- ExpenseReceipt = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({
336
- tableName: "expense_receipts",
337
- api: { include: [] },
338
- mcp: { include: [] },
339
- cli: false,
340
- indexes: [{
341
- name: "expense_receipts_expense_sha256_key",
342
- columns: ["expenseId", "contentSha256"],
343
- unique: true
344
- }, {
345
- name: "expense_receipts_tenant_sha256_idx",
346
- columns: ["tenantId", "contentSha256"]
347
- }]
348
- })], ExpenseReceipt);
349
- //#endregion
350
- //#region src/collections/ExpenseReceiptCollection.ts
351
- var ExpenseReceiptCollection_exports = /* @__PURE__ */ __exportAll({ ExpenseReceiptCollection: () => ExpenseReceiptCollection });
352
- var __defProp = Object.defineProperty;
353
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
354
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
355
- enumerable: true,
356
- configurable: true,
357
- writable: true,
358
- value
359
- }) : obj[key] = value;
360
- var __decorateClass = (decorators, target, key, kind) => {
361
- var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
362
- for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
363
- if (kind && result) __defProp(target, key, result);
364
- return result;
365
- };
366
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
367
- var DEFAULT_DUPLICATE_LIMIT = 100;
368
- var MAX_DUPLICATE_LIMIT = 1e3;
369
- var ExpenseReceiptCollection = class extends SmrtJunction {
370
- leftField = "expenseId";
371
- rightField = "assetId";
372
- sortField = "createdAt";
373
- positionField = null;
374
- /**
375
- * Attach a receipt file to an expense.
376
- *
377
- * Does not change the expense in any way — in particular not its review
378
- * state.
379
- *
380
- * @throws {DuplicateReceiptError} when this expense already carries a file
381
- * with the same SHA-256.
382
- */
383
- async attachReceipt(input) {
384
- const receipt = await this.create({
385
- expenseId: input.expenseId,
386
- assetId: input.assetId,
387
- contentSha256: input.contentSha256,
388
- uploadedByProfileId: input.uploadedByProfileId ?? null,
389
- filename: input.filename ?? "",
390
- mimeType: input.mimeType ?? "",
391
- byteCount: input.byteCount ?? 0
392
- });
393
- await receipt.save();
394
- return receipt;
395
- }
396
- /**
397
- * Refused: a receipt is evidence and needs its content hash, which a
398
- * generic junction attach does not carry. Use {@link attachReceipt}.
399
- *
400
- * @throws {ExpenseError} `EXPENSE_RECEIPT_INVALID`, always.
401
- */
402
- async attach(_expenseId, _assetId, _opts = {}) {
403
- throw new ExpenseError("EXPENSE_RECEIPT_INVALID", "ExpenseReceiptCollection.attach() is not supported: use attachReceipt(), which records and checks the file hash.");
404
- }
405
- /**
406
- * Refused before anything is deleted. The inherited replace-all deletes
407
- * every receipt of the expense and re-attaches without hashes, which would
408
- * lose the evidence. Attach with {@link attachReceipt}; remove a link with
409
- * `detach()`.
410
- *
411
- * @throws {ExpenseError} `EXPENSE_RECEIPT_INVALID`, always.
412
- */
413
- async setLinks(_expenseId, _assetIds, _opts = {}) {
414
- throw new ExpenseError("EXPENSE_RECEIPT_INVALID", "ExpenseReceiptCollection.setLinks() is not supported: it would delete receipt evidence. Use attachReceipt() to add and detach() to remove.");
415
- }
416
- /** Receipts of one expense, oldest first. */
417
- async receiptsFor(expenseId) {
418
- return this.byLeft(expenseId);
419
- }
420
- /**
421
- * Files (by SHA-256) attached to more than one expense, for a person to
422
- * review. Nothing is changed or rejected: the same receipt on two expenses
423
- * may be a split cost or a double claim, and only a reviewer can tell.
424
- *
425
- * With `expenseId`, reports only files that expense shares with others;
426
- * without it, scans the visible tenant (largest groups first).
427
- */
428
- async findDuplicateReceipts(options = {}) {
429
- const limit = Math.min(Math.max(Math.trunc(options.limit ?? DEFAULT_DUPLICATE_LIMIT), 1), MAX_DUPLICATE_LIMIT);
430
- let hashes;
431
- if (options.expenseId) {
432
- const own = await this.byLeft(options.expenseId);
433
- hashes = [...new Set(own.map((receipt) => receipt.contentSha256))];
434
- } else {
435
- const [facet] = await this.facets({ fields: [{
436
- field: "contentSha256",
437
- limit: MAX_DUPLICATE_LIMIT
438
- }] });
439
- hashes = (facet?.values ?? []).filter((value) => value.count > 1).map((value) => String(value.value)).slice(0, limit);
440
- }
441
- if (hashes.length === 0) return [];
442
- const rows = await this.list({
443
- where: { contentSha256: hashes },
444
- orderBy: ["content_sha256 ASC", "created_at ASC"]
445
- });
446
- const byHash = /* @__PURE__ */ new Map();
447
- for (const receipt of rows) {
448
- const group = byHash.get(receipt.contentSha256) ?? [];
449
- group.push(receipt);
450
- byHash.set(receipt.contentSha256, group);
451
- }
452
- const groups = [];
453
- for (const [contentSha256, receipts] of byHash) {
454
- const expenseIds = [...new Set(receipts.map((receipt) => receipt.expenseId))].sort();
455
- if (expenseIds.length < 2) continue;
456
- groups.push({
457
- contentSha256,
458
- expenseIds,
459
- receipts
460
- });
461
- }
462
- groups.sort((a, b) => b.expenseIds.length - a.expenseIds.length || a.contentSha256.localeCompare(b.contentSha256));
463
- return groups.slice(0, limit);
464
- }
297
+ amount: { order: 2 },
298
+ currency: { order: 3 },
299
+ incurredOn: {
300
+ label: "Date",
301
+ order: 4,
302
+ help: "The date on the receipt, as YYYY-MM-DD."
303
+ },
304
+ category: {
305
+ order: 5,
306
+ help: "One word, such as material, travel, freight or other."
307
+ },
308
+ vendorId: {
309
+ label: "Vendor",
310
+ order: 6
311
+ },
312
+ paidBy: { order: 7 },
313
+ paidByProfileId: {
314
+ label: "Person who paid",
315
+ order: 8,
316
+ help: "Who paid out of their own pocket, when a person paid."
317
+ },
318
+ paidByName: { order: 9 },
319
+ reimbursable: { order: 10 },
320
+ costObjectId: {
321
+ label: "Charged to",
322
+ order: 11,
323
+ help: "The job, project or client the cost belongs to."
324
+ },
325
+ costObjectType: { visibility: "hidden" },
326
+ financialBreakdown: { visibility: "hidden" },
327
+ refundOfId: { visibility: "hidden" },
328
+ recordedByProfileId: { visibility: "hidden" },
329
+ commitmentId: { visibility: "hidden" },
330
+ commitmentLineId: { visibility: "hidden" },
331
+ reviewedByProfileId: { visibility: "hidden" },
332
+ duplicateOfId: { visibility: "hidden" }
333
+ } },
334
+ ExpenseReceipt: { fields: {
335
+ contentSha256: { visibility: "hidden" },
336
+ byteCount: { visibility: "hidden" },
337
+ uploadedByProfileId: { visibility: "hidden" }
338
+ } }
339
+ };
465
340
  };
466
- __publicField(ExpenseReceiptCollection, "__smrtPackage__", "@happyvertical/smrt-expenses");
467
- __publicField(ExpenseReceiptCollection, "_itemClass", ExpenseReceipt);
468
- ExpenseReceiptCollection = __decorateClass([smrt({
469
- api: { include: [] },
470
- mcp: { include: [] },
471
- cli: false
472
- })], ExpenseReceiptCollection);
473
341
  //#endregion
474
- export { DuplicateReceiptError, EXPENSE_PAYERS, EXPENSE_REVIEW_STATUSES, Expense, ExpenseCollection, ExpenseError, ExpenseReceipt, ExpenseReceiptCollection, SUGGESTED_EXPENSE_CATEGORIES, computeContentSha256, ExpenseCollection_exports as t };
342
+ export { DuplicateReceiptError, EXPENSE_PAYERS, EXPENSE_REVIEW_STATUSES, Expense, ExpenseActionError, ExpenseCollection, ExpenseError, ExpenseReceipt, ExpenseReceiptCollection, ExpensesRecipe, SUGGESTED_EXPENSE_CATEGORIES, computeContentSha256, ExpenseCollection_exports as t };
475
343
 
476
344
  //# sourceMappingURL=index.js.map