Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { accountingApi } from "@/lib/accounting-api";
|
|
import { useTenantId } from "@/hooks/useTenantId";
|
|
import {
|
|
PageHeader,
|
|
DataTable,
|
|
EmptyState,
|
|
LoadingState,
|
|
ErrorState,
|
|
JalaliDateText,
|
|
Tabs,
|
|
Badge,
|
|
} from "@/components/ds";
|
|
|
|
type TabId = "compliance" | "posting";
|
|
|
|
export default function AuditPage() {
|
|
const { tenantId } = useTenantId();
|
|
const [tab, setTab] = useState<TabId>("compliance");
|
|
|
|
const complianceQ = useQuery({
|
|
queryKey: ["accounting", tenantId, "compliance-audit"],
|
|
queryFn: () => accountingApi.compliance.listAudit(tenantId!),
|
|
enabled: !!tenantId && tab === "compliance",
|
|
});
|
|
|
|
const postingQ = useQuery({
|
|
queryKey: ["accounting", tenantId, "posting-audit-logs"],
|
|
queryFn: () => accountingApi.vouchers.auditLogs(tenantId!),
|
|
enabled: !!tenantId && tab === "posting",
|
|
});
|
|
|
|
const activeQ = tab === "compliance" ? complianceQ : postingQ;
|
|
|
|
if (!tenantId) return <LoadingState />;
|
|
if (activeQ.isLoading) return <LoadingState />;
|
|
if (activeQ.error) {
|
|
return <ErrorState message={activeQ.error.message} onRetry={() => activeQ.refetch()} />;
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<PageHeader
|
|
title="حسابرسی و انطباق"
|
|
description="رکوردهای Compliance و لاگهای ثبت اسناد."
|
|
/>
|
|
|
|
<Tabs
|
|
className="mb-6"
|
|
value={tab}
|
|
onChange={(id) => setTab(id as TabId)}
|
|
items={[
|
|
{ id: "compliance", label: "رکوردهای انطباق" },
|
|
{ id: "posting", label: "لاگ ثبت اسناد" },
|
|
]}
|
|
/>
|
|
|
|
{tab === "compliance" && (
|
|
<DataTable
|
|
columns={[
|
|
{
|
|
key: "created_at",
|
|
header: "زمان",
|
|
render: (r) => <JalaliDateText value={String(r.created_at ?? "")} withTime />,
|
|
},
|
|
{ key: "action", header: "عملیات" },
|
|
{ key: "resource_type", header: "منبع" },
|
|
{
|
|
key: "actor_user_id",
|
|
header: "عامل",
|
|
render: (r) => (
|
|
<span className="font-mono text-xs" dir="ltr">
|
|
{String(r.actor_user_id ?? "—")}
|
|
</span>
|
|
),
|
|
},
|
|
]}
|
|
rows={(complianceQ.data ?? []) as unknown as Record<string, unknown>[]}
|
|
empty={
|
|
<EmptyState
|
|
title="رکورد حسابرسی نیست"
|
|
description="پس از عملیات مالی، رکوردها اینجا ظاهر میشوند."
|
|
/>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{tab === "posting" && (
|
|
<DataTable
|
|
columns={[
|
|
{
|
|
key: "created_at",
|
|
header: "زمان",
|
|
render: (r) => <JalaliDateText value={String(r.created_at ?? "")} withTime />,
|
|
},
|
|
{
|
|
key: "operation",
|
|
header: "عملیات",
|
|
render: (r) => <Badge>{String(r.operation)}</Badge>,
|
|
},
|
|
{ key: "voucher_number", header: "شماره سند", render: (r) => String(r.voucher_number ?? "—") },
|
|
{ key: "resource_type", header: "نوع منبع" },
|
|
{
|
|
key: "resource_id",
|
|
header: "شناسه",
|
|
render: (r) => (
|
|
<span className="font-mono text-xs" dir="ltr">
|
|
{String(r.resource_id ?? "—")}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: "actor_user_id",
|
|
header: "عامل",
|
|
render: (r) => (
|
|
<span className="font-mono text-xs" dir="ltr">
|
|
{String(r.actor_user_id ?? "—")}
|
|
</span>
|
|
),
|
|
},
|
|
]}
|
|
rows={(postingQ.data ?? []) as unknown as Record<string, unknown>[]}
|
|
empty={
|
|
<EmptyState
|
|
title="لاگی ثبت نشده"
|
|
description="پس از ایجاد، ویرایش یا ثبت اسناد، لاگها اینجا نمایش داده میشوند."
|
|
/>
|
|
}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|