mirror of
https://github.com/vas3k/TaxHacker.git
synced 2026-01-08 06:29:43 -06:00
feat: select columns to show
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { transactionFormSchema } from "@/forms/transactions"
|
||||
import { FILE_UPLOAD_PATH, getTransactionFileUploadPath } from "@/lib/files"
|
||||
import { updateField } from "@/models/fields"
|
||||
import { createFile, deleteFile } from "@/models/files"
|
||||
import {
|
||||
bulkDeleteTransactions,
|
||||
@@ -160,3 +161,15 @@ export async function bulkDeleteTransactionsAction(transactionIds: string[]) {
|
||||
return { success: false, error: "Failed to delete transactions" }
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateFieldVisibilityAction(fieldCode: string, isVisible: boolean) {
|
||||
try {
|
||||
await updateField(fieldCode, {
|
||||
isVisibleInList: isVisible,
|
||||
})
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
console.error("Failed to update field visibility:", error)
|
||||
return { success: false, error: "Failed to update field visibility" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,10 @@ export default async function TransactionsPage({ searchParams }: { searchParams:
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<TransactionSearchAndFilters categories={categories} projects={projects} />
|
||||
<TransactionSearchAndFilters categories={categories} projects={projects} fields={fields} />
|
||||
|
||||
<main>
|
||||
<TransactionList transactions={transactions} />
|
||||
<TransactionList transactions={transactions} fields={fields} />
|
||||
|
||||
{transactions.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center gap-2 h-full min-h-[400px]">
|
||||
|
||||
65
components/transactions/fields-selector.tsx
Normal file
65
components/transactions/fields-selector.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
"use client"
|
||||
|
||||
import { updateFieldVisibilityAction } from "@/app/transactions/actions"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { Field } from "@prisma/client"
|
||||
import { ColumnsIcon } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
|
||||
export function ColumnSelector({ fields, onChange }: { fields: Field[]; onChange?: () => void }) {
|
||||
const router = useRouter()
|
||||
const [isLoading, setIsLoading] = useState<{ [key: string]: boolean }>({})
|
||||
|
||||
const handleToggle = async (fieldCode: string, isCurrentlyVisible: boolean) => {
|
||||
setIsLoading((prev) => ({ ...prev, [fieldCode]: true }))
|
||||
|
||||
try {
|
||||
await updateFieldVisibilityAction(fieldCode, !isCurrentlyVisible)
|
||||
|
||||
// Refresh the page to reflect changes
|
||||
if (onChange) {
|
||||
onChange()
|
||||
} else {
|
||||
router.refresh()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to toggle column visibility:", error)
|
||||
} finally {
|
||||
setIsLoading((prev) => ({ ...prev, [fieldCode]: false }))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" title="Select table columns">
|
||||
<ColumnsIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuLabel>Show Columns</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{fields.map((field) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={field.code}
|
||||
checked={field.isVisibleInList}
|
||||
onCheckedChange={() => handleToggle(field.code, field.isVisibleInList)}
|
||||
disabled={isLoading[field.code]}
|
||||
>
|
||||
{field.name}
|
||||
{isLoading[field.code] && <span className="ml-2 text-xs opacity-50">Saving...</span>}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
"use client"
|
||||
|
||||
import { DateRangePicker } from "@/components/forms/date-range-picker"
|
||||
import { ColumnSelector } from "@/components/transactions/fields-selector"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { isFiltered, useTransactionFilters } from "@/hooks/use-transaction-filters"
|
||||
import { TransactionFilters } from "@/models/transactions"
|
||||
import { Category, Project } from "@prisma/client"
|
||||
import { Category, Field, Project } from "@prisma/client"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
export function TransactionSearchAndFilters({ categories, projects }: { categories: Category[]; projects: Project[] }) {
|
||||
export function TransactionSearchAndFilters({
|
||||
categories,
|
||||
projects,
|
||||
fields,
|
||||
}: {
|
||||
categories: Category[]
|
||||
projects: Project[]
|
||||
fields: Field[]
|
||||
}) {
|
||||
const [filters, setFilters] = useTransactionFilters()
|
||||
|
||||
const handleFilterChange = (name: keyof TransactionFilters, value: any) => {
|
||||
@@ -95,6 +104,8 @@ export function TransactionSearchAndFilters({ categories, projects }: { categori
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<ColumnSelector fields={fields} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -6,36 +6,47 @@ import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { calcTotalPerCurrency } from "@/lib/stats"
|
||||
import { cn, formatCurrency } from "@/lib/utils"
|
||||
import { Category, Project, Transaction } from "@prisma/client"
|
||||
import { Category, Field, Project, Transaction } from "@prisma/client"
|
||||
import { formatDate } from "date-fns"
|
||||
import { ArrowDownIcon, ArrowUpIcon, File } from "lucide-react"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
|
||||
export const transactionsTable = [
|
||||
{
|
||||
type FieldRenderer = {
|
||||
name: string
|
||||
code: string
|
||||
classes?: string
|
||||
sortable: boolean
|
||||
formatValue?: (transaction: Transaction & any) => React.ReactNode
|
||||
footerValue?: (transactions: Transaction[]) => React.ReactNode
|
||||
}
|
||||
|
||||
export const standardFieldRenderers: Record<string, FieldRenderer> = {
|
||||
name: {
|
||||
name: "Name",
|
||||
db: "name",
|
||||
code: "name",
|
||||
classes: "font-medium max-w-[300px] min-w-[120px] overflow-hidden",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
merchant: {
|
||||
name: "Merchant",
|
||||
db: "merchant",
|
||||
classes: "max-w-[200px] max-h-[20px] min-w-[120px] overflow-hidden",
|
||||
code: "merchant",
|
||||
classes: "max-w-[200px] max-h-[20px] min-w-[120px] overflow-hidden",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
issuedAt: {
|
||||
name: "Date",
|
||||
db: "issuedAt",
|
||||
code: "issuedAt",
|
||||
classes: "min-w-[100px]",
|
||||
format: (transaction: Transaction) => (transaction.issuedAt ? formatDate(transaction.issuedAt, "yyyy-MM-dd") : ""),
|
||||
sortable: true,
|
||||
formatValue: (transaction: Transaction) =>
|
||||
transaction.issuedAt ? formatDate(transaction.issuedAt, "yyyy-MM-dd") : "",
|
||||
},
|
||||
{
|
||||
projectCode: {
|
||||
name: "Project",
|
||||
db: "projectCode",
|
||||
format: (transaction: Transaction & { project: Project }) =>
|
||||
code: "projectCode",
|
||||
sortable: true,
|
||||
formatValue: (transaction: Transaction & { project: Project }) =>
|
||||
transaction.projectCode ? (
|
||||
<Badge className="whitespace-nowrap" style={{ backgroundColor: transaction.project?.color }}>
|
||||
{transaction.project?.name || ""}
|
||||
@@ -43,12 +54,12 @@ export const transactionsTable = [
|
||||
) : (
|
||||
"-"
|
||||
),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
categoryCode: {
|
||||
name: "Category",
|
||||
db: "categoryCode",
|
||||
format: (transaction: Transaction & { category: Category }) =>
|
||||
code: "categoryCode",
|
||||
sortable: true,
|
||||
formatValue: (transaction: Transaction & { category: Category }) =>
|
||||
transaction.categoryCode ? (
|
||||
<Badge className="whitespace-nowrap" style={{ backgroundColor: transaction.category?.color }}>
|
||||
{transaction.category?.name || ""}
|
||||
@@ -56,24 +67,24 @@ export const transactionsTable = [
|
||||
) : (
|
||||
"-"
|
||||
),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
files: {
|
||||
name: "Files",
|
||||
db: "files",
|
||||
format: (transaction: Transaction) => (
|
||||
code: "files",
|
||||
sortable: false,
|
||||
formatValue: (transaction: Transaction) => (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<File className="w-4 h-4" />
|
||||
{(transaction.files as string[]).length}
|
||||
</div>
|
||||
),
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
total: {
|
||||
name: "Total",
|
||||
db: "total",
|
||||
code: "total",
|
||||
classes: "text-right",
|
||||
format: (transaction: Transaction) => (
|
||||
sortable: true,
|
||||
formatValue: (transaction: Transaction) => (
|
||||
<div className="text-right text-lg">
|
||||
<div
|
||||
className={cn(
|
||||
@@ -96,8 +107,7 @@ export const transactionsTable = [
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
sortable: true,
|
||||
footer: (transactions: Transaction[]) => {
|
||||
footerValue: (transactions: Transaction[]) => {
|
||||
const totalPerCurrency = calcTotalPerCurrency(transactions)
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
@@ -110,12 +120,26 @@ export const transactionsTable = [
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function TransactionList({ transactions }: { transactions: Transaction[] }) {
|
||||
const getFieldRenderer = (field: Field): FieldRenderer => {
|
||||
if (standardFieldRenderers[field.code as keyof typeof standardFieldRenderers]) {
|
||||
return standardFieldRenderers[field.code as keyof typeof standardFieldRenderers]
|
||||
} else {
|
||||
return {
|
||||
name: field.name,
|
||||
code: field.code,
|
||||
classes: "",
|
||||
sortable: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function TransactionList({ transactions, fields = [] }: { transactions: Transaction[]; fields?: Field[] }) {
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([])
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
const [sorting, setSorting] = useState<{ field: string | null; direction: "asc" | "desc" | null }>(() => {
|
||||
const ordering = searchParams.get("ordering")
|
||||
if (!ordering) return { field: null, direction: null }
|
||||
@@ -126,7 +150,18 @@ export function TransactionList({ transactions }: { transactions: Transaction[]
|
||||
}
|
||||
})
|
||||
|
||||
const toggleAll = () => {
|
||||
const visibleFields = useMemo(
|
||||
() =>
|
||||
fields
|
||||
.filter((field) => field.isVisibleInList)
|
||||
.map((field) => ({
|
||||
...field,
|
||||
renderer: getFieldRenderer(field),
|
||||
})),
|
||||
[fields]
|
||||
)
|
||||
|
||||
const toggleAllRows = () => {
|
||||
if (selectedIds.length === transactions.length) {
|
||||
setSelectedIds([])
|
||||
} else {
|
||||
@@ -134,7 +169,7 @@ export function TransactionList({ transactions }: { transactions: Transaction[]
|
||||
}
|
||||
}
|
||||
|
||||
const toggleOne = (e: React.MouseEvent, id: string) => {
|
||||
const toggleOneRow = (e: React.MouseEvent, id: string) => {
|
||||
e.stopPropagation()
|
||||
if (selectedIds.includes(id)) {
|
||||
setSelectedIds(selectedIds.filter((item) => item !== id))
|
||||
@@ -187,16 +222,19 @@ export function TransactionList({ transactions }: { transactions: Transaction[]
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[50px] select-none">
|
||||
<Checkbox checked={selectedIds.length === transactions.length} onCheckedChange={toggleAll} />
|
||||
<Checkbox checked={selectedIds.length === transactions.length} onCheckedChange={toggleAllRows} />
|
||||
</TableHead>
|
||||
{transactionsTable.map((field) => (
|
||||
{visibleFields.map((field) => (
|
||||
<TableHead
|
||||
key={field.db}
|
||||
className={cn(field.classes, field.sortable && "hover:cursor-pointer hover:bg-accent select-none")}
|
||||
onClick={() => field.sortable && handleSort(field.db)}
|
||||
key={field.code}
|
||||
className={cn(
|
||||
field.renderer.classes,
|
||||
field.renderer.sortable && "hover:cursor-pointer hover:bg-accent select-none"
|
||||
)}
|
||||
onClick={() => field.renderer.sortable && handleSort(field.code)}
|
||||
>
|
||||
{field.name}
|
||||
{field.sortable && getSortIcon(field.db)}
|
||||
{field.renderer.name}
|
||||
{field.renderer.sortable && getSortIcon(field.code)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
@@ -213,14 +251,14 @@ export function TransactionList({ transactions }: { transactions: Transaction[]
|
||||
checked={selectedIds.includes(transaction.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked !== "indeterminate") {
|
||||
toggleOne({ stopPropagation: () => {} } as React.MouseEvent, transaction.id)
|
||||
toggleOneRow({ stopPropagation: () => {} } as React.MouseEvent, transaction.id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
{transactionsTable.map((field) => (
|
||||
<TableCell key={field.db} className={field.classes}>
|
||||
{field.format ? field.format(transaction) : transaction[field.db]}
|
||||
{visibleFields.map((field) => (
|
||||
<TableCell key={field.code} className={field.renderer.classes}>
|
||||
{field.renderer.formatValue ? field.renderer.formatValue(transaction) : transaction[field.code]}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
@@ -229,9 +267,9 @@ export function TransactionList({ transactions }: { transactions: Transaction[]
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell></TableCell>
|
||||
{transactionsTable.map((field) => (
|
||||
<TableCell key={field.db} className={field.classes}>
|
||||
{field.footer ? field.footer(transactions) : ""}
|
||||
{visibleFields.map((field) => (
|
||||
<TableCell key={field.code} className={field.renderer.classes}>
|
||||
{field.renderer.footerValue ? field.renderer.footerValue(transactions) : ""}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
|
||||
@@ -9,20 +9,27 @@ export type ExportFilters = TransactionFilters
|
||||
|
||||
export type ExportFields = string[]
|
||||
|
||||
export const exportImportFields = [
|
||||
{
|
||||
export type ExportImportFieldSettings = {
|
||||
code: string
|
||||
type: string
|
||||
export?: (value: any) => Promise<any>
|
||||
import?: (value: any) => Promise<any>
|
||||
}
|
||||
|
||||
export const exportImportFieldsMapping: Record<string, ExportImportFieldSettings> = {
|
||||
name: {
|
||||
code: "name",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
description: {
|
||||
code: "description",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
merchant: {
|
||||
code: "merchant",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
total: {
|
||||
code: "total",
|
||||
type: "number",
|
||||
export: async function (value: number) {
|
||||
@@ -33,11 +40,11 @@ export const exportImportFields = [
|
||||
return isNaN(num) ? 0.0 : num * 100
|
||||
},
|
||||
},
|
||||
{
|
||||
currencyCode: {
|
||||
code: "currencyCode",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
convertedTotal: {
|
||||
code: "convertedTotal",
|
||||
type: "number",
|
||||
export: async function (value: number | null) {
|
||||
@@ -51,19 +58,19 @@ export const exportImportFields = [
|
||||
return isNaN(num) ? 0.0 : num * 100
|
||||
},
|
||||
},
|
||||
{
|
||||
convertedCurrencyCode: {
|
||||
code: "convertedCurrencyCode",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: {
|
||||
code: "type",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
note: {
|
||||
code: "note",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
categoryCode: {
|
||||
code: "categoryCode",
|
||||
type: "string",
|
||||
export: async function (value: string | null) {
|
||||
@@ -78,7 +85,7 @@ export const exportImportFields = [
|
||||
return category?.code
|
||||
},
|
||||
},
|
||||
{
|
||||
projectCode: {
|
||||
code: "projectCode",
|
||||
type: "string",
|
||||
export: async function (value: string | null) {
|
||||
@@ -93,7 +100,7 @@ export const exportImportFields = [
|
||||
return project?.code
|
||||
},
|
||||
},
|
||||
{
|
||||
issuedAt: {
|
||||
code: "issuedAt",
|
||||
type: "date",
|
||||
export: async function (value: Date | null) {
|
||||
@@ -115,12 +122,7 @@ export const exportImportFields = [
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const exportImportFieldsMapping = exportImportFields.reduce((acc, field) => {
|
||||
acc[field.code] = field
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
}
|
||||
|
||||
export const importProject = async (name: string) => {
|
||||
const code = codeFromName(name)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_fields" (
|
||||
"code" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL DEFAULT 'string',
|
||||
"llm_prompt" TEXT,
|
||||
"options" JSONB,
|
||||
"is_visible_in_list" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_visible_in_analysis" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_required" BOOLEAN NOT NULL DEFAULT false,
|
||||
"is_extra" BOOLEAN NOT NULL DEFAULT true
|
||||
);
|
||||
INSERT INTO "new_fields" ("code", "is_extra", "is_required", "llm_prompt", "name", "options", "type") SELECT "code", "is_extra", "is_required", "llm_prompt", "name", "options", "type" FROM "fields";
|
||||
DROP TABLE "fields";
|
||||
ALTER TABLE "new_fields" RENAME TO "fields";
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
@@ -42,13 +42,15 @@ model Project {
|
||||
}
|
||||
|
||||
model Field {
|
||||
code String @id
|
||||
name String
|
||||
type String @default("string")
|
||||
llm_prompt String?
|
||||
options Json?
|
||||
isRequired Boolean @default(false) @map("is_required")
|
||||
isExtra Boolean @default(true) @map("is_extra")
|
||||
code String @id
|
||||
name String
|
||||
type String @default("string")
|
||||
llm_prompt String?
|
||||
options Json?
|
||||
isVisibleInList Boolean @default(false) @map("is_visible_in_list")
|
||||
isVisibleInAnalysis Boolean @default(false) @map("is_visible_in_analysis")
|
||||
isRequired Boolean @default(false) @map("is_required")
|
||||
isExtra Boolean @default(true) @map("is_extra")
|
||||
|
||||
@@map("fields")
|
||||
}
|
||||
|
||||
132
prisma/seed.js
132
prisma/seed.js
@@ -1,6 +1,6 @@
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL || "file:./db.sqlite"
|
||||
const DATABASE_URL = process.env.DATABASE_URL
|
||||
const prisma = new PrismaClient({
|
||||
datasources: {
|
||||
db: {
|
||||
@@ -306,6 +306,8 @@ const fields = [
|
||||
name: "Name",
|
||||
type: "string",
|
||||
llm_prompt: "human readable name, summarize what is bought in the invoice",
|
||||
isVisibleInList: true,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: true,
|
||||
isExtra: false,
|
||||
},
|
||||
@@ -314,6 +316,8 @@ const fields = [
|
||||
name: "Description",
|
||||
type: "string",
|
||||
llm_prompt: "description of the transaction",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
@@ -322,54 +326,8 @@ const fields = [
|
||||
name: "Merchant",
|
||||
type: "string",
|
||||
llm_prompt: "merchant name, use the original spelling and language",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "type",
|
||||
name: "Type",
|
||||
type: "string",
|
||||
llm_prompt: "",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "total",
|
||||
name: "Total",
|
||||
type: "number",
|
||||
llm_prompt: "total total of the transaction",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "currencyCode",
|
||||
name: "Currency",
|
||||
type: "string",
|
||||
llm_prompt: "currency code, ISO 4217 three letter code like USD, EUR, including crypto codes like BTC, ETH, etc",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "convertedTotal",
|
||||
name: "Converted Total",
|
||||
type: "number",
|
||||
llm_prompt: "",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "convertedCurrencyCode",
|
||||
name: "Converted Currency Code",
|
||||
type: "string",
|
||||
llm_prompt: "",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "note",
|
||||
name: "Note",
|
||||
type: "string",
|
||||
llm_prompt: "",
|
||||
isVisibleInList: true,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
@@ -378,6 +336,8 @@ const fields = [
|
||||
name: "Category",
|
||||
type: "string",
|
||||
llm_prompt: "category code, one of: {categories.code}",
|
||||
isVisibleInList: true,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
@@ -386,6 +346,8 @@ const fields = [
|
||||
name: "Project",
|
||||
type: "string",
|
||||
llm_prompt: "project code, one of: {projects.code}",
|
||||
isVisibleInList: true,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
@@ -394,14 +356,68 @@ const fields = [
|
||||
name: "Issued At",
|
||||
type: "string",
|
||||
llm_prompt: "issued at date (YYYY-MM-DD format)",
|
||||
isVisibleInList: true,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "text",
|
||||
name: "Extracted Text",
|
||||
code: "total",
|
||||
name: "Total",
|
||||
type: "number",
|
||||
llm_prompt: "total total of the transaction",
|
||||
isVisibleInList: true,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "currencyCode",
|
||||
name: "Currency",
|
||||
type: "string",
|
||||
llm_prompt: "extract all recognised text from the invoice",
|
||||
llm_prompt: "currency code, ISO 4217 three letter code like USD, EUR, including crypto codes like BTC, ETH, etc",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "convertedTotal",
|
||||
name: "Converted Total",
|
||||
type: "number",
|
||||
llm_prompt: "",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: false,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "convertedCurrencyCode",
|
||||
name: "Converted Currency Code",
|
||||
type: "string",
|
||||
llm_prompt: "",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: false,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "type",
|
||||
name: "Type",
|
||||
type: "string",
|
||||
llm_prompt: "",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: true,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
{
|
||||
code: "note",
|
||||
name: "Note",
|
||||
type: "string",
|
||||
llm_prompt: "",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: false,
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
@@ -410,9 +426,19 @@ const fields = [
|
||||
name: "VAT Amount",
|
||||
type: "number",
|
||||
llm_prompt: "total VAT total in currency of the invoice",
|
||||
isVisibleInList: false,
|
||||
isVisibleInAnalysis: false,
|
||||
isRequired: false,
|
||||
isExtra: true,
|
||||
},
|
||||
{
|
||||
code: "text",
|
||||
name: "Extracted Text",
|
||||
type: "string",
|
||||
llm_prompt: "extract all recognised text from the invoice",
|
||||
isRequired: false,
|
||||
isExtra: false,
|
||||
},
|
||||
]
|
||||
|
||||
async function isDatabaseEmpty() {
|
||||
@@ -464,6 +490,8 @@ async function main() {
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
llm_prompt: field.llm_prompt,
|
||||
isVisibleInList: field.isVisibleInList,
|
||||
isVisibleInAnalysis: field.isVisibleInAnalysis,
|
||||
isRequired: field.isRequired,
|
||||
isExtra: field.isExtra,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user