mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Accounting: unify vendor roster + COA across bill-approvals and accounting bills
Single vendor source (public.vendors) and single COA source (accounting.accounts) across both bill flows: - Forward sync now carries public.bills.expense_account_id into the mirrored accounting.bill_items.account_id (when it resolves to accounting.accounts). - Reverse trigger flows a GL change on a mirrored accounting bill line back to public.bills.expense_account_id (loop-guarded). - New public.ensure_accounting_vendor RPC resolves a chosen public vendor to its accounting.vendors row; one-time backfill of mirrored line account_id. - BillApprovalsPage GL pickers now use ChartOfAccountsDropdown (accounting.accounts). - AccountingBillsPage vendor picker now lists public.vendors scoped to the company's association and maps to accounting.vendors on save. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import ChartOfAccountsDropdown from "@/components/ChartOfAccountsDropdown.jsx";
|
||||
const statusColors: Record<string, string> = {
|
||||
pending: "bg-amber-100 text-amber-700",
|
||||
approved: "bg-emerald-100 text-emerald-700",
|
||||
@@ -604,19 +605,6 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
||||
return bill.status;
|
||||
};
|
||||
|
||||
const selectedSystem = associations.find((a: any) => a.id === form.association_id)?.accounting_system ?? null;
|
||||
const filteredAccounts = form.association_id
|
||||
? accounts.filter((a: any) => {
|
||||
// Platform associations use the accounts managed in the accounting
|
||||
// dashboard (synced into chart_of_accounts as accounting_system 'platform').
|
||||
if (selectedSystem === "platform") {
|
||||
return a.accounting_system === "platform" && a.association_id === form.association_id;
|
||||
}
|
||||
// Buildium / Zoho keep their existing scoping; never surface platform-only rows.
|
||||
return a.accounting_system !== "platform" && (!a.association_id || a.association_id === form.association_id);
|
||||
})
|
||||
: [];
|
||||
|
||||
const filteredVendors = form.association_id
|
||||
? vendors.filter((v: any) => v.association_id === form.association_id || (Array.isArray(v.association_ids) && v.association_ids.includes(form.association_id)))
|
||||
: [];
|
||||
@@ -1162,20 +1150,14 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
||||
{/* GL Account */}
|
||||
<div>
|
||||
<Label>GL Account (Expense) <span className="text-destructive">*</span></Label>
|
||||
<Select
|
||||
<ChartOfAccountsDropdown
|
||||
accountType="expense"
|
||||
associationId={form.association_id || null}
|
||||
value={form.expense_account_id}
|
||||
onValueChange={(v) => setForm({ ...form, expense_account_id: v })}
|
||||
onChange={(v: string) => setForm({ ...form, expense_account_id: v })}
|
||||
disabled={!form.association_id}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={form.association_id ? "Select GL Account" : "Select a client first"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{filteredAccounts.map((a: any) => (
|
||||
<SelectItem key={a.id} value={a.id}>{a.account_number} - {a.account_name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
placeholder={form.association_id ? "Select GL Account" : "Select a client first"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Request Approval From */}
|
||||
@@ -1410,12 +1392,14 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
||||
</div>
|
||||
<div>
|
||||
<Label>GL Account (Expense)</Label>
|
||||
<Select value={form.expense_account_id} onValueChange={(v) => setForm({ ...form, expense_account_id: v })} disabled={!form.association_id}>
|
||||
<SelectTrigger><SelectValue placeholder="Select GL Account" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
{filteredAccounts.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.account_number} - {a.account_name}</SelectItem>)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<ChartOfAccountsDropdown
|
||||
accountType="expense"
|
||||
associationId={form.association_id || null}
|
||||
value={form.expense_account_id}
|
||||
onChange={(v: string) => setForm({ ...form, expense_account_id: v })}
|
||||
disabled={!form.association_id}
|
||||
placeholder="Select GL Account"
|
||||
/>
|
||||
</div>
|
||||
{vendorNotFound && (
|
||||
<Alert variant="destructive" className="border-amber-300 bg-amber-50 text-amber-900">
|
||||
|
||||
Reference in New Issue
Block a user