Complete Accounting sidebar modules with ops CRUD, BFF proxy, and treasury APIs.
Wire nested nav pages for phases 5.1-5.11 to real list/create endpoints, fix COA import connectivity via same-origin proxy, and add operational migration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
12c8615615
commit
7d66932da8
@ -0,0 +1,73 @@
|
||||
"""Alembic: operational docs, inventory items, warehouses."""
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
from app.models.types import GUID
|
||||
|
||||
revision = "0003_operational"
|
||||
down_revision = "0002_phases_57_511"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"business_documents",
|
||||
sa.Column("id", GUID(), primary_key=True),
|
||||
sa.Column("tenant_id", GUID(), nullable=False),
|
||||
sa.Column("module", sa.String(40), nullable=False),
|
||||
sa.Column("doc_type", sa.String(50), nullable=False),
|
||||
sa.Column("number", sa.String(50), nullable=False),
|
||||
sa.Column("doc_date", sa.Date(), nullable=False),
|
||||
sa.Column("party_name", sa.String(255), nullable=True),
|
||||
sa.Column("party_id", GUID(), nullable=True),
|
||||
sa.Column("amount", sa.Numeric(18, 4), nullable=False, server_default="0"),
|
||||
sa.Column("status", sa.String(30), nullable=False, server_default="draft"),
|
||||
sa.Column("description", sa.Text(), nullable=True),
|
||||
sa.Column("meta_json", sa.Text(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("tenant_id", "module", "number", name="uq_biz_doc_tenant_module_number"),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_biz_doc_tenant_module_type",
|
||||
"business_documents",
|
||||
["tenant_id", "module", "doc_type"],
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"inventory_items",
|
||||
sa.Column("id", GUID(), primary_key=True),
|
||||
sa.Column("tenant_id", GUID(), nullable=False),
|
||||
sa.Column("code", sa.String(50), nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("item_type", sa.String(30), nullable=False, server_default="goods"),
|
||||
sa.Column("unit", sa.String(30), nullable=False, server_default="عدد"),
|
||||
sa.Column("quantity_on_hand", sa.Numeric(18, 4), nullable=False, server_default="0"),
|
||||
sa.Column("unit_cost", sa.Numeric(18, 4), nullable=False, server_default="0"),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("tenant_id", "code", name="uq_inventory_item_tenant_code"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"warehouses",
|
||||
sa.Column("id", GUID(), primary_key=True),
|
||||
sa.Column("tenant_id", GUID(), nullable=False),
|
||||
sa.Column("code", sa.String(50), nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("tenant_id", "code", name="uq_warehouse_tenant_code"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("warehouses")
|
||||
op.drop_table("inventory_items")
|
||||
op.drop_index("ix_biz_doc_tenant_module_type", table_name="business_documents")
|
||||
op.drop_table("business_documents")
|
||||
@ -23,7 +23,7 @@ __all__ = [
|
||||
|
||||
def get_pagination(
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
page_size: int = Query(default=20, ge=1, le=500),
|
||||
) -> PaginationParams:
|
||||
return PaginationParams(page=page, page_size=page_size)
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ from app.api.v1 import (
|
||||
fixed_assets,
|
||||
health,
|
||||
ledger,
|
||||
operational,
|
||||
payroll,
|
||||
posting,
|
||||
purchase_inventory,
|
||||
@ -31,3 +32,4 @@ api_router.include_router(payroll.router, prefix="/payroll", tags=["payroll"])
|
||||
api_router.include_router(reporting.router, prefix="/reporting", tags=["reporting"])
|
||||
api_router.include_router(compliance.router, prefix="/compliance", tags=["compliance"])
|
||||
api_router.include_router(setup.router, prefix="/setup", tags=["setup"])
|
||||
api_router.include_router(operational.router, prefix="/ops", tags=["ops"])
|
||||
|
||||
@ -105,6 +105,25 @@ async def create_audit_record(
|
||||
return {"id": str(record.id)}
|
||||
|
||||
|
||||
@router.get("/policies")
|
||||
async def list_compliance_policies(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
rows = await CompliancePolicyRepo(db).list_by_tenant(tenant_id, limit=200)
|
||||
return [
|
||||
{
|
||||
"id": str(p.id),
|
||||
"code": p.code,
|
||||
"name": p.name,
|
||||
"policy_type": p.policy_type,
|
||||
"is_active": getattr(p, "is_active", True),
|
||||
}
|
||||
for p in rows
|
||||
]
|
||||
|
||||
|
||||
@router.post("/policies", status_code=status.HTTP_201_CREATED)
|
||||
async def create_compliance_policy(
|
||||
body: CompliancePolicyCreate,
|
||||
|
||||
218
backend/services/accounting/app/api/v1/operational.py
Normal file
218
backend/services/accounting/app/api/v1/operational.py
Normal file
@ -0,0 +1,218 @@
|
||||
"""Operational documents + inventory masters API."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.api.deps import get_db, require_tenant
|
||||
from app.core.security import get_current_user
|
||||
from app.models.operational import BusinessDocument, InventoryItem, Warehouse
|
||||
from app.repositories.base import TenantBaseRepository
|
||||
from shared.exceptions import NotFoundError
|
||||
from shared.security import CurrentUser
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class BizDocCreate(BaseModel):
|
||||
module: str = Field(max_length=40)
|
||||
doc_type: str = Field(max_length=50)
|
||||
number: str = Field(max_length=50)
|
||||
doc_date: date
|
||||
party_name: str | None = None
|
||||
party_id: UUID | None = None
|
||||
amount: Decimal = Decimal("0")
|
||||
status: str = "draft"
|
||||
description: str | None = None
|
||||
meta_json: str | None = None
|
||||
|
||||
|
||||
class BizDocUpdate(BaseModel):
|
||||
party_name: str | None = None
|
||||
amount: Decimal | None = None
|
||||
status: str | None = None
|
||||
description: str | None = None
|
||||
doc_date: date | None = None
|
||||
meta_json: str | None = None
|
||||
|
||||
|
||||
class BizDocRead(BaseModel):
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
module: str
|
||||
doc_type: str
|
||||
number: str
|
||||
doc_date: date
|
||||
party_name: str | None
|
||||
amount: Decimal
|
||||
status: str
|
||||
description: str | None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class ItemCreate(BaseModel):
|
||||
code: str
|
||||
name: str
|
||||
item_type: str = "goods"
|
||||
unit: str = "عدد"
|
||||
quantity_on_hand: Decimal = Decimal("0")
|
||||
unit_cost: Decimal = Decimal("0")
|
||||
|
||||
|
||||
class ItemRead(BaseModel):
|
||||
id: UUID
|
||||
code: str
|
||||
name: str
|
||||
item_type: str
|
||||
unit: str
|
||||
quantity_on_hand: Decimal
|
||||
unit_cost: Decimal
|
||||
is_active: bool
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class WarehouseCreate(BaseModel):
|
||||
code: str
|
||||
name: str
|
||||
|
||||
|
||||
class WarehouseRead(BaseModel):
|
||||
id: UUID
|
||||
code: str
|
||||
name: str
|
||||
is_active: bool
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class BizDocRepo(TenantBaseRepository[BusinessDocument]):
|
||||
model = BusinessDocument
|
||||
|
||||
|
||||
class ItemRepo(TenantBaseRepository[InventoryItem]):
|
||||
model = InventoryItem
|
||||
|
||||
|
||||
class WarehouseRepo(TenantBaseRepository[Warehouse]):
|
||||
model = Warehouse
|
||||
|
||||
|
||||
@router.get("/documents", response_model=list[BizDocRead])
|
||||
async def list_documents(
|
||||
module: str | None = Query(default=None),
|
||||
doc_type: str | None = Query(default=None),
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
stmt = select(BusinessDocument).where(BusinessDocument.tenant_id == tenant_id)
|
||||
if module:
|
||||
stmt = stmt.where(BusinessDocument.module == module)
|
||||
if doc_type:
|
||||
stmt = stmt.where(BusinessDocument.doc_type == doc_type)
|
||||
stmt = stmt.order_by(BusinessDocument.doc_date.desc()).limit(200)
|
||||
result = await db.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
@router.post("/documents", response_model=BizDocRead, status_code=status.HTTP_201_CREATED)
|
||||
async def create_document(
|
||||
body: BizDocCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = BusinessDocument(tenant_id=tenant_id, **body.model_dump())
|
||||
await BizDocRepo(db).add(entity)
|
||||
await db.commit()
|
||||
await db.refresh(entity)
|
||||
return entity
|
||||
|
||||
|
||||
@router.patch("/documents/{doc_id}", response_model=BizDocRead)
|
||||
async def update_document(
|
||||
doc_id: UUID,
|
||||
body: BizDocUpdate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
repo = BizDocRepo(db)
|
||||
entity = await repo.get(tenant_id, doc_id)
|
||||
if entity is None:
|
||||
raise NotFoundError("سند یافت نشد", error_code="document_not_found")
|
||||
for k, v in body.model_dump(exclude_unset=True).items():
|
||||
setattr(entity, k, v)
|
||||
await db.commit()
|
||||
await db.refresh(entity)
|
||||
return entity
|
||||
|
||||
|
||||
@router.post("/documents/{doc_id}/confirm", response_model=BizDocRead)
|
||||
async def confirm_document(
|
||||
doc_id: UUID,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = await BizDocRepo(db).get(tenant_id, doc_id)
|
||||
if entity is None:
|
||||
raise NotFoundError("سند یافت نشد", error_code="document_not_found")
|
||||
entity.status = "confirmed"
|
||||
await db.commit()
|
||||
await db.refresh(entity)
|
||||
return entity
|
||||
|
||||
|
||||
@router.get("/items", response_model=list[ItemRead])
|
||||
async def list_items(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
return await ItemRepo(db).list_by_tenant(tenant_id, limit=200)
|
||||
|
||||
|
||||
@router.post("/items", response_model=ItemRead, status_code=status.HTTP_201_CREATED)
|
||||
async def create_item(
|
||||
body: ItemCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = InventoryItem(tenant_id=tenant_id, **body.model_dump())
|
||||
await ItemRepo(db).add(entity)
|
||||
await db.commit()
|
||||
await db.refresh(entity)
|
||||
return entity
|
||||
|
||||
|
||||
@router.get("/warehouses", response_model=list[WarehouseRead])
|
||||
async def list_warehouses(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
return await WarehouseRepo(db).list_by_tenant(tenant_id, limit=100)
|
||||
|
||||
|
||||
@router.post("/warehouses", response_model=WarehouseRead, status_code=status.HTTP_201_CREATED)
|
||||
async def create_warehouse(
|
||||
body: WarehouseCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = Warehouse(tenant_id=tenant_id, **body.model_dump())
|
||||
await WarehouseRepo(db).add(entity)
|
||||
await db.commit()
|
||||
await db.refresh(entity)
|
||||
return entity
|
||||
@ -204,3 +204,256 @@ async def update_cash_box(
|
||||
await db.commit()
|
||||
await db.refresh(entity)
|
||||
return CashBoxRead.model_validate(entity)
|
||||
|
||||
|
||||
# ── Cheques / transfers / receipts / payments / reconciliation ───────────────
|
||||
|
||||
from app.models.treasury import Cheque, PaymentVoucher, ReceiptVoucher, Reconciliation, Transfer
|
||||
from app.models.types import ChequeStatus, ReconciliationStatus
|
||||
|
||||
|
||||
class ChequeRepo(TenantBaseRepository[Cheque]):
|
||||
model = Cheque
|
||||
|
||||
|
||||
class TransferRepo(TenantBaseRepository[Transfer]):
|
||||
model = Transfer
|
||||
|
||||
|
||||
class ReceiptRepo(TenantBaseRepository[ReceiptVoucher]):
|
||||
model = ReceiptVoucher
|
||||
|
||||
|
||||
class PaymentRepo(TenantBaseRepository[PaymentVoucher]):
|
||||
model = PaymentVoucher
|
||||
|
||||
|
||||
class ReconciliationRepo(TenantBaseRepository[Reconciliation]):
|
||||
model = Reconciliation
|
||||
|
||||
|
||||
class ChequeCreate(BaseModel):
|
||||
cheque_number: str
|
||||
amount: Decimal
|
||||
issue_date: date
|
||||
due_date: date | None = None
|
||||
is_incoming: bool = True
|
||||
payee: str | None = None
|
||||
bank_account_id: UUID | None = None
|
||||
status: ChequeStatus = ChequeStatus.RECEIVED
|
||||
|
||||
|
||||
class TransferCreate(BaseModel):
|
||||
transfer_date: date
|
||||
amount: Decimal
|
||||
from_type: str
|
||||
from_id: UUID
|
||||
to_type: str
|
||||
to_id: UUID
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class ReceiptCreate(BaseModel):
|
||||
receipt_number: str
|
||||
receipt_date: date
|
||||
amount: Decimal
|
||||
payment_method: str = "cash"
|
||||
cash_box_id: UUID | None = None
|
||||
bank_account_id: UUID | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class PaymentCreate(BaseModel):
|
||||
payment_number: str
|
||||
payment_date: date
|
||||
amount: Decimal
|
||||
payment_method: str = "cash"
|
||||
cash_box_id: UUID | None = None
|
||||
bank_account_id: UUID | None = None
|
||||
description: str | None = None
|
||||
|
||||
|
||||
class ReconciliationCreate(BaseModel):
|
||||
bank_account_id: UUID
|
||||
statement_date: date
|
||||
statement_balance: Decimal
|
||||
book_balance: Decimal
|
||||
|
||||
|
||||
@router.get("/cheques")
|
||||
async def list_cheques(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
rows = await ChequeRepo(db).list_by_tenant(tenant_id, limit=200)
|
||||
return [
|
||||
{
|
||||
"id": str(c.id),
|
||||
"cheque_number": c.cheque_number,
|
||||
"amount": str(c.amount),
|
||||
"issue_date": c.issue_date.isoformat(),
|
||||
"due_date": c.due_date.isoformat() if c.due_date else None,
|
||||
"status": c.status.value,
|
||||
"is_incoming": c.is_incoming,
|
||||
"payee": c.payee,
|
||||
}
|
||||
for c in rows
|
||||
]
|
||||
|
||||
|
||||
@router.post("/cheques", status_code=status.HTTP_201_CREATED)
|
||||
async def create_cheque(
|
||||
body: ChequeCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = Cheque(tenant_id=tenant_id, **body.model_dump())
|
||||
await ChequeRepo(db).add(entity)
|
||||
await db.commit()
|
||||
return {"id": str(entity.id), "cheque_number": entity.cheque_number}
|
||||
|
||||
|
||||
@router.get("/transfers")
|
||||
async def list_transfers(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
rows = await TransferRepo(db).list_by_tenant(tenant_id, limit=200)
|
||||
return [
|
||||
{
|
||||
"id": str(t.id),
|
||||
"transfer_date": t.transfer_date.isoformat(),
|
||||
"amount": str(t.amount),
|
||||
"from_type": t.from_type,
|
||||
"from_id": str(t.from_id),
|
||||
"to_type": t.to_type,
|
||||
"to_id": str(t.to_id),
|
||||
"description": t.description,
|
||||
}
|
||||
for t in rows
|
||||
]
|
||||
|
||||
|
||||
@router.post("/transfers", status_code=status.HTTP_201_CREATED)
|
||||
async def create_transfer(
|
||||
body: TransferCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = Transfer(tenant_id=tenant_id, **body.model_dump())
|
||||
await TransferRepo(db).add(entity)
|
||||
await db.commit()
|
||||
return {"id": str(entity.id)}
|
||||
|
||||
|
||||
@router.get("/receipts")
|
||||
async def list_receipts(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
rows = await ReceiptRepo(db).list_by_tenant(tenant_id, limit=200)
|
||||
return [
|
||||
{
|
||||
"id": str(r.id),
|
||||
"receipt_number": r.receipt_number,
|
||||
"receipt_date": r.receipt_date.isoformat(),
|
||||
"amount": str(r.amount),
|
||||
"payment_method": r.payment_method,
|
||||
"description": r.description,
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
|
||||
|
||||
@router.post("/receipts", status_code=status.HTTP_201_CREATED)
|
||||
async def create_receipt(
|
||||
body: ReceiptCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = ReceiptVoucher(tenant_id=tenant_id, **body.model_dump())
|
||||
await ReceiptRepo(db).add(entity)
|
||||
await db.commit()
|
||||
return {"id": str(entity.id)}
|
||||
|
||||
|
||||
@router.get("/payments")
|
||||
async def list_payments(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
rows = await PaymentRepo(db).list_by_tenant(tenant_id, limit=200)
|
||||
return [
|
||||
{
|
||||
"id": str(p.id),
|
||||
"payment_number": p.payment_number,
|
||||
"payment_date": p.payment_date.isoformat(),
|
||||
"amount": str(p.amount),
|
||||
"payment_method": p.payment_method,
|
||||
"description": p.description,
|
||||
}
|
||||
for p in rows
|
||||
]
|
||||
|
||||
|
||||
@router.post("/payments", status_code=status.HTTP_201_CREATED)
|
||||
async def create_payment(
|
||||
body: PaymentCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
entity = PaymentVoucher(tenant_id=tenant_id, **body.model_dump())
|
||||
await PaymentRepo(db).add(entity)
|
||||
await db.commit()
|
||||
return {"id": str(entity.id)}
|
||||
|
||||
|
||||
@router.get("/reconciliations")
|
||||
async def list_reconciliations(
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
rows = await ReconciliationRepo(db).list_by_tenant(tenant_id, limit=100)
|
||||
return [
|
||||
{
|
||||
"id": str(r.id),
|
||||
"bank_account_id": str(r.bank_account_id),
|
||||
"statement_date": r.statement_date.isoformat(),
|
||||
"statement_balance": str(r.statement_balance),
|
||||
"book_balance": str(r.book_balance),
|
||||
"difference": str(r.difference),
|
||||
"status": r.status.value,
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
|
||||
|
||||
@router.post("/reconciliations", status_code=status.HTTP_201_CREATED)
|
||||
async def create_reconciliation(
|
||||
body: ReconciliationCreate,
|
||||
tenant_id: UUID = Depends(require_tenant),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_user: CurrentUser = Depends(get_current_user),
|
||||
):
|
||||
diff = body.statement_balance - body.book_balance
|
||||
entity = Reconciliation(
|
||||
tenant_id=tenant_id,
|
||||
bank_account_id=body.bank_account_id,
|
||||
statement_date=body.statement_date,
|
||||
statement_balance=body.statement_balance,
|
||||
book_balance=body.book_balance,
|
||||
difference=diff,
|
||||
status=ReconciliationStatus.PENDING,
|
||||
)
|
||||
await ReconciliationRepo(db).add(entity)
|
||||
await db.commit()
|
||||
return {"id": str(entity.id), "difference": str(diff)}
|
||||
|
||||
@ -140,3 +140,8 @@ from app.models.treasury import ( # noqa: F401
|
||||
Transfer,
|
||||
TreasurySettings,
|
||||
)
|
||||
from app.models.operational import ( # noqa: F401
|
||||
BusinessDocument,
|
||||
InventoryItem,
|
||||
Warehouse,
|
||||
)
|
||||
|
||||
63
backend/services/accounting/app/models/operational.py
Normal file
63
backend/services/accounting/app/models/operational.py
Normal file
@ -0,0 +1,63 @@
|
||||
"""Phase ops — lightweight business documents for Sales/Purchase/Inventory UI."""
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import Boolean, Date, Index, Numeric, String, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.database import Base
|
||||
from app.models.base import TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin
|
||||
from app.models.types import GUID
|
||||
|
||||
|
||||
class BusinessDocument(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
|
||||
"""Generic operational document used by Sales / Purchase / Inventory screens."""
|
||||
|
||||
__tablename__ = "business_documents"
|
||||
|
||||
module: Mapped[str] = mapped_column(String(40), nullable=False)
|
||||
doc_type: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
number: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
doc_date: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
party_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
party_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
|
||||
amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(30), default="draft", nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
meta_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "module", "number", name="uq_biz_doc_tenant_module_number"),
|
||||
Index("ix_biz_doc_tenant_module_type", "tenant_id", "module", "doc_type"),
|
||||
)
|
||||
|
||||
|
||||
class InventoryItem(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
|
||||
__tablename__ = "inventory_items"
|
||||
|
||||
code: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
item_type: Mapped[str] = mapped_column(String(30), default="goods", nullable=False)
|
||||
unit: Mapped[str] = mapped_column(String(30), default="عدد", nullable=False)
|
||||
quantity_on_hand: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
|
||||
unit_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "code", name="uq_inventory_item_tenant_code"),
|
||||
)
|
||||
|
||||
|
||||
class Warehouse(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
|
||||
__tablename__ = "warehouses"
|
||||
|
||||
code: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint("tenant_id", "code", name="uq_warehouse_tenant_code"),
|
||||
)
|
||||
@ -283,6 +283,11 @@ async def import_coa_template(
|
||||
status_code=422,
|
||||
error_code="parent_missing",
|
||||
)
|
||||
existing_acc = await account_repo.get_by_code(tenant_id, item.code)
|
||||
if existing_acc is not None:
|
||||
# Reuse existing account code so re-import / overlapping COA does not crash
|
||||
code_to_id[item.code] = existing_acc.id
|
||||
continue
|
||||
acc = Account(
|
||||
tenant_id=tenant_id,
|
||||
chart_id=chart.id,
|
||||
|
||||
@ -11,7 +11,7 @@ from pydantic import BaseModel, Field
|
||||
T = TypeVar("T")
|
||||
|
||||
DEFAULT_PAGE_SIZE = 20
|
||||
MAX_PAGE_SIZE = 100
|
||||
MAX_PAGE_SIZE = 500
|
||||
|
||||
|
||||
class PaginationParams(BaseModel):
|
||||
|
||||
@ -189,6 +189,7 @@ services:
|
||||
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID: ${NEXT_PUBLIC_KEYCLOAK_CLIENT_ID:-superapp-frontend}
|
||||
NEXT_PUBLIC_PLATFORM_BASE_DOMAIN: ${NEXT_PUBLIC_PLATFORM_BASE_DOMAIN:-torbatyar.ir}
|
||||
NEXT_PUBLIC_ACCOUNTING_API_URL: ${NEXT_PUBLIC_ACCOUNTING_API_URL:-http://localhost:8002}
|
||||
ACCOUNTING_SERVICE_URL: ${ACCOUNTING_SERVICE_URL:-http://accounting-service:8002}
|
||||
# polling برای hot-reload پایدار روی volume mount ویندوز
|
||||
WATCHPACK_POLLING: "true"
|
||||
CHOKIDAR_USEPOLLING: "true"
|
||||
|
||||
7
frontend/app/accounting/ai/analytics/page.tsx
Normal file
7
frontend/app/accounting/ai/analytics/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="تحلیل هوشمند" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/anomalies/page.tsx
Normal file
7
frontend/app/accounting/ai/anomalies/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="کشف ناهنجاری" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/documents/page.tsx
Normal file
7
frontend/app/accounting/ai/documents/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="تحلیل اسناد" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/forecasts/page.tsx
Normal file
7
frontend/app/accounting/ai/forecasts/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="پیشبینیها" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/page.tsx
Normal file
7
frontend/app/accounting/ai/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="دستیار حسابداری" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/qa/page.tsx
Normal file
7
frontend/app/accounting/ai/qa/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="پرسش و پاسخ" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/recommendations/page.tsx
Normal file
7
frontend/app/accounting/ai/recommendations/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="پیشنهادهای هوشمند" />;
|
||||
}
|
||||
7
frontend/app/accounting/ai/reconciliation/page.tsx
Normal file
7
frontend/app/accounting/ai/reconciliation/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { BlockedModulePage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <BlockedModulePage title="تطبیق هوشمند" />;
|
||||
}
|
||||
13
frontend/app/accounting/assets/accumulated/page.tsx
Normal file
13
frontend/app/accounting/assets/accumulated/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="استهلاک انباشته"
|
||||
module="assets"
|
||||
docType="accumulated"
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
frontend/app/accounting/assets/dashboard/page.tsx
Normal file
5
frontend/app/accounting/assets/dashboard/page.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
redirect("/accounting/assets");
|
||||
}
|
||||
5
frontend/app/accounting/assets/depreciate/page.tsx
Normal file
5
frontend/app/accounting/assets/depreciate/page.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
redirect("/accounting/assets");
|
||||
}
|
||||
13
frontend/app/accounting/assets/dispose/page.tsx
Normal file
13
frontend/app/accounting/assets/dispose/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="معرفی و فروش دارایی"
|
||||
module="assets"
|
||||
docType="dispose"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/assets/schedule/page.tsx
Normal file
13
frontend/app/accounting/assets/schedule/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="برنامه استهلاک"
|
||||
module="assets"
|
||||
docType="schedule"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/assets/transfers/page.tsx
Normal file
13
frontend/app/accounting/assets/transfers/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="انتقال دارایی"
|
||||
module="assets"
|
||||
docType="transfer"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/assets/valuation/page.tsx
Normal file
13
frontend/app/accounting/assets/valuation/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="ارزشگذاری دارایی"
|
||||
module="assets"
|
||||
docType="valuation"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/control/page.tsx
Normal file
13
frontend/app/accounting/budget/control/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="کنترل بودجه"
|
||||
module="budget"
|
||||
docType="control"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/definitions/page.tsx
Normal file
13
frontend/app/accounting/budget/definitions/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تعریف بودجه"
|
||||
module="budget"
|
||||
docType="definition"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/forecast/page.tsx
Normal file
13
frontend/app/accounting/budget/forecast/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="پیشبینی مالی"
|
||||
module="budget"
|
||||
docType="forecast"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/operational/page.tsx
Normal file
13
frontend/app/accounting/budget/operational/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="بودجهریزی عملیاتی"
|
||||
module="budget"
|
||||
docType="operational"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/performance/page.tsx
Normal file
13
frontend/app/accounting/budget/performance/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="مقایسه عملکرد"
|
||||
module="budget"
|
||||
docType="performance"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/reports/page.tsx
Normal file
13
frontend/app/accounting/budget/reports/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گزارش بودجه"
|
||||
module="budget"
|
||||
docType="report"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/scenarios/page.tsx
Normal file
13
frontend/app/accounting/budget/scenarios/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="سناریوهای مالی"
|
||||
module="budget"
|
||||
docType="scenario"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/budget/variance/page.tsx
Normal file
13
frontend/app/accounting/budget/variance/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تحلیل انحراف بودجه"
|
||||
module="budget"
|
||||
docType="variance"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/approvals/page.tsx
Normal file
13
frontend/app/accounting/compliance/approvals/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گردش تأیید"
|
||||
module="compliance"
|
||||
docType="approval"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/controls/page.tsx
Normal file
13
frontend/app/accounting/compliance/controls/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="کنترلهای داخلی"
|
||||
module="compliance"
|
||||
docType="control"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/policies/page.tsx
Normal file
13
frontend/app/accounting/compliance/policies/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="سیاستها و قوانین"
|
||||
module="compliance"
|
||||
docType="policy"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/records/page.tsx
Normal file
13
frontend/app/accounting/compliance/records/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اسناد و سوابق"
|
||||
module="compliance"
|
||||
docType="record"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/risks/page.tsx
Normal file
13
frontend/app/accounting/compliance/risks/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="مدیریت ریسک"
|
||||
module="compliance"
|
||||
docType="risk"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/sod/page.tsx
Normal file
13
frontend/app/accounting/compliance/sod/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تفکیک وظایف (SoD)"
|
||||
module="compliance"
|
||||
docType="sod"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/compliance/violations/page.tsx
Normal file
13
frontend/app/accounting/compliance/violations/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گزارش تخلفات"
|
||||
module="compliance"
|
||||
docType="violation"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/categories/page.tsx
Normal file
13
frontend/app/accounting/documents/categories/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="دستهبندی اسناد"
|
||||
module="documents"
|
||||
docType="category"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/financial/page.tsx
Normal file
13
frontend/app/accounting/documents/financial/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اسناد مالی"
|
||||
module="documents"
|
||||
docType="financial"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/hr/page.tsx
Normal file
13
frontend/app/accounting/documents/hr/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اسناد پرسنلی"
|
||||
module="documents"
|
||||
docType="hr"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/purchase/page.tsx
Normal file
13
frontend/app/accounting/documents/purchase/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اسناد خرید"
|
||||
module="documents"
|
||||
docType="purchase"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/retention/page.tsx
Normal file
13
frontend/app/accounting/documents/retention/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="نگهداری اسناد"
|
||||
module="documents"
|
||||
docType="retention"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/sales/page.tsx
Normal file
13
frontend/app/accounting/documents/sales/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اسناد فروش"
|
||||
module="documents"
|
||||
docType="sales"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/search/page.tsx
Normal file
13
frontend/app/accounting/documents/search/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="جستجوی اسناد"
|
||||
module="documents"
|
||||
docType="search"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/documents/versions/page.tsx
Normal file
13
frontend/app/accounting/documents/versions/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="نسخههای سند"
|
||||
module="documents"
|
||||
docType="version"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/bank/page.tsx
Normal file
13
frontend/app/accounting/integration/bank/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اتصال بانک"
|
||||
module="integration"
|
||||
docType="bank"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/crm/page.tsx
Normal file
13
frontend/app/accounting/integration/crm/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اتصال CRM"
|
||||
module="integration"
|
||||
docType="crm"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/gateway/page.tsx
Normal file
13
frontend/app/accounting/integration/gateway/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="درگاه پرداخت"
|
||||
module="integration"
|
||||
docType="gateway"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/import-export/page.tsx
Normal file
13
frontend/app/accounting/integration/import-export/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="ورود / خروج داده"
|
||||
module="integration"
|
||||
docType="import_export"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/inventory/page.tsx
Normal file
13
frontend/app/accounting/integration/inventory/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اتصال انبار"
|
||||
module="integration"
|
||||
docType="inventory"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/payroll/page.tsx
Normal file
13
frontend/app/accounting/integration/payroll/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اتصال حقوق"
|
||||
module="integration"
|
||||
docType="payroll"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/reports/page.tsx
Normal file
13
frontend/app/accounting/integration/reports/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گزارش یکپارچگی"
|
||||
module="integration"
|
||||
docType="report"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/integration/webhooks/page.tsx
Normal file
13
frontend/app/accounting/integration/webhooks/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="وبسرویسها"
|
||||
module="integration"
|
||||
docType="webhook"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/inventory/adjustments/page.tsx
Normal file
13
frontend/app/accounting/inventory/adjustments/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تعدیل موجودی"
|
||||
module="inventory"
|
||||
docType="adjustment"
|
||||
/>
|
||||
);
|
||||
}
|
||||
7
frontend/app/accounting/inventory/items/page.tsx
Normal file
7
frontend/app/accounting/inventory/items/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { InventoryItemsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <InventoryItemsPage />;
|
||||
}
|
||||
13
frontend/app/accounting/inventory/movements/page.tsx
Normal file
13
frontend/app/accounting/inventory/movements/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گردش کالا"
|
||||
module="inventory"
|
||||
docType="movement"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/inventory/receipts-issues/page.tsx
Normal file
13
frontend/app/accounting/inventory/receipts-issues/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="رسید ورود و خروج"
|
||||
module="inventory"
|
||||
docType="receipt_issue"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/inventory/reports/page.tsx
Normal file
13
frontend/app/accounting/inventory/reports/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گزارشهای انبار"
|
||||
module="inventory"
|
||||
docType="report"
|
||||
/>
|
||||
);
|
||||
}
|
||||
7
frontend/app/accounting/inventory/settings/page.tsx
Normal file
7
frontend/app/accounting/inventory/settings/page.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { WarehousesPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return <WarehousesPage />;
|
||||
}
|
||||
13
frontend/app/accounting/inventory/transfers/page.tsx
Normal file
13
frontend/app/accounting/inventory/transfers/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="انتقال انبار"
|
||||
module="inventory"
|
||||
docType="transfer"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/inventory/valuation/page.tsx
Normal file
13
frontend/app/accounting/inventory/valuation/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="ارزشگذاری موجودی"
|
||||
module="inventory"
|
||||
docType="valuation"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/activity/page.tsx
Normal file
13
frontend/app/accounting/monitoring/activity/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="فعالیت کاربران"
|
||||
module="monitoring"
|
||||
docType="activity"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/alerts/page.tsx
Normal file
13
frontend/app/accounting/monitoring/alerts/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="هشدارها"
|
||||
module="monitoring"
|
||||
docType="alert"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/health/page.tsx
Normal file
13
frontend/app/accounting/monitoring/health/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="وضعیت سرویس"
|
||||
module="monitoring"
|
||||
docType="health"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/kpis/page.tsx
Normal file
13
frontend/app/accounting/monitoring/kpis/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="شاخصهای کلیدی"
|
||||
module="monitoring"
|
||||
docType="kpi"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/page.tsx
Normal file
13
frontend/app/accounting/monitoring/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="داشبورد نظارتی"
|
||||
module="monitoring"
|
||||
docType="dashboard"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/performance/page.tsx
Normal file
13
frontend/app/accounting/monitoring/performance/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="عملکرد سیستم"
|
||||
module="monitoring"
|
||||
docType="performance"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/monitoring/recent/page.tsx
Normal file
13
frontend/app/accounting/monitoring/recent/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="عملیات اخیر"
|
||||
module="monitoring"
|
||||
docType="recent"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/payroll/allocation/page.tsx
Normal file
13
frontend/app/accounting/payroll/allocation/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تسهیم هزینه حقوق"
|
||||
module="payroll"
|
||||
docType="allocation"
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
frontend/app/accounting/payroll/calculate/page.tsx
Normal file
5
frontend/app/accounting/payroll/calculate/page.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
redirect("/accounting/payroll");
|
||||
}
|
||||
13
frontend/app/accounting/payroll/contracts/page.tsx
Normal file
13
frontend/app/accounting/payroll/contracts/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تعریف قرارداد"
|
||||
module="payroll"
|
||||
docType="contract"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/payroll/employees/page.tsx
Normal file
13
frontend/app/accounting/payroll/employees/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="کارمندان"
|
||||
module="payroll"
|
||||
docType="employee"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/payroll/items/page.tsx
Normal file
13
frontend/app/accounting/payroll/items/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اقلام حقوق و مزایا"
|
||||
module="payroll"
|
||||
docType="pay_item"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/payroll/payments/page.tsx
Normal file
13
frontend/app/accounting/payroll/payments/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="پرداخت حقوق"
|
||||
module="payroll"
|
||||
docType="payment"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/payroll/reports/page.tsx
Normal file
13
frontend/app/accounting/payroll/reports/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="گزارشهای حقوق"
|
||||
module="payroll"
|
||||
docType="report"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/payroll/structures/page.tsx
Normal file
13
frontend/app/accounting/payroll/structures/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="ساختار حقوق"
|
||||
module="payroll"
|
||||
docType="structure"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/goods-receipts/page.tsx
Normal file
13
frontend/app/accounting/purchase/goods-receipts/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="رسید کالا"
|
||||
module="purchase"
|
||||
docType="goods_receipt"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/invoices/page.tsx
Normal file
13
frontend/app/accounting/purchase/invoices/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="فاکتور خرید"
|
||||
module="purchase"
|
||||
docType="invoice"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/orders/page.tsx
Normal file
13
frontend/app/accounting/purchase/orders/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="سفارش خرید"
|
||||
module="purchase"
|
||||
docType="order"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/prepayments/page.tsx
Normal file
13
frontend/app/accounting/purchase/prepayments/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="پیشپرداختها"
|
||||
module="purchase"
|
||||
docType="prepayment"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/requests/page.tsx
Normal file
13
frontend/app/accounting/purchase/requests/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="درخواست خرید"
|
||||
module="purchase"
|
||||
docType="request"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/returns/page.tsx
Normal file
13
frontend/app/accounting/purchase/returns/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="مرجوعی خرید"
|
||||
module="purchase"
|
||||
docType="return"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/purchase/settlements/page.tsx
Normal file
13
frontend/app/accounting/purchase/settlements/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تسویه با تأمینکننده"
|
||||
module="purchase"
|
||||
docType="settlement"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo, useState, Suspense } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import { accountingApi } from "@/lib/accounting-api";
|
||||
import { useTenantId } from "@/hooks/useTenantId";
|
||||
import { formatMoney } from "@/lib/utils";
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
import {
|
||||
PageHeader,
|
||||
Button,
|
||||
@ -30,6 +32,14 @@ const LABELS: Record<ReportKind, string> = {
|
||||
"income-statement": "سود و زیان",
|
||||
};
|
||||
|
||||
const OPS_REPORTS: Record<string, { title: string; module: string; docType: string }> = {
|
||||
"cash-flow": { title: "جریان وجوه نقد", module: "reports", docType: "cash_flow" },
|
||||
equity: { title: "تغییرات حقوق صاحبان سهام", module: "reports", docType: "equity" },
|
||||
subsidiary: { title: "گزارش معین", module: "reports", docType: "subsidiary" },
|
||||
detail: { title: "گزارش تفصیلی", module: "reports", docType: "detail" },
|
||||
analytical: { title: "گزارش تحلیلی", module: "reports", docType: "analytical" },
|
||||
};
|
||||
|
||||
const FIELD_LABELS: Record<string, string> = {
|
||||
total_debit: "جمع بدهکار",
|
||||
total_credit: "جمع بستانکار",
|
||||
@ -66,7 +76,7 @@ function formatCellValue(key: string, value: unknown): React.ReactNode {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export default function ReportsPage() {
|
||||
function EngineReports({ initialKind }: { initialKind?: string }) {
|
||||
const { tenantId } = useTenantId();
|
||||
const [periodId, setPeriodId] = useState("");
|
||||
const [result, setResult] = useState<{ kind: ReportKind; payload: { id: string; data: unknown } } | null>(
|
||||
@ -105,10 +115,7 @@ export default function ReportsPage() {
|
||||
accountingApi.reporting.export(tenantId!, result!.payload.id, format),
|
||||
onSuccess: (data) => {
|
||||
toast.success(`خروجی ${data.format} ثبت شد`);
|
||||
if (data.file_path) {
|
||||
toast.message(data.file_path);
|
||||
}
|
||||
// Download client-side JSON snapshot for immediate UX
|
||||
if (data.file_path) toast.message(data.file_path);
|
||||
if (result) {
|
||||
const blob = new Blob([JSON.stringify(result.payload, null, 2)], {
|
||||
type: "application/json",
|
||||
@ -144,6 +151,13 @@ export default function ReportsPage() {
|
||||
return <ErrorState message={periodsQ.error.message} onRetry={() => periodsQ.refetch()} />;
|
||||
}
|
||||
|
||||
const highlight =
|
||||
initialKind === "trial-balance" ||
|
||||
initialKind === "balance-sheet" ||
|
||||
initialKind === "income-statement"
|
||||
? (initialKind as ReportKind)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
@ -168,7 +182,7 @@ export default function ReportsPage() {
|
||||
<Button
|
||||
key={kind}
|
||||
type="button"
|
||||
variant="outline"
|
||||
variant={highlight === kind ? "default" : "outline"}
|
||||
disabled={runM.isPending || !periodId}
|
||||
onClick={() => runM.mutate(kind)}
|
||||
>
|
||||
@ -233,3 +247,21 @@ export default function ReportsPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ReportsRouter() {
|
||||
const sp = useSearchParams();
|
||||
const type = sp.get("type") || "";
|
||||
const ops = OPS_REPORTS[type];
|
||||
if (ops) {
|
||||
return <BusinessDocsPage title={ops.title} module={ops.module} docType={ops.docType} />;
|
||||
}
|
||||
return <EngineReports initialKind={type || undefined} />;
|
||||
}
|
||||
|
||||
export default function ReportsPage() {
|
||||
return (
|
||||
<Suspense fallback={<LoadingState />}>
|
||||
<ReportsRouter />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
13
frontend/app/accounting/sales/invoices/page.tsx
Normal file
13
frontend/app/accounting/sales/invoices/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="فاکتور فروش"
|
||||
module="sales"
|
||||
docType="invoice"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/sales/opportunities/page.tsx
Normal file
13
frontend/app/accounting/sales/opportunities/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="فرصتهای فروش"
|
||||
module="sales"
|
||||
docType="opportunity"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/sales/orders/page.tsx
Normal file
13
frontend/app/accounting/sales/orders/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="سفارش فروش"
|
||||
module="sales"
|
||||
docType="order"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/sales/pre-receipts/page.tsx
Normal file
13
frontend/app/accounting/sales/pre-receipts/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="پیشدریافتها"
|
||||
module="sales"
|
||||
docType="pre_receipt"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/sales/proformas/page.tsx
Normal file
13
frontend/app/accounting/sales/proformas/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="پیشفاکتور"
|
||||
module="sales"
|
||||
docType="proforma"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/sales/returns/page.tsx
Normal file
13
frontend/app/accounting/sales/returns/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="مرجوعی فروش"
|
||||
module="sales"
|
||||
docType="return"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/sales/settlements/page.tsx
Normal file
13
frontend/app/accounting/sales/settlements/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تسویه حساب مشتریان"
|
||||
module="sales"
|
||||
docType="settlement"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/settings/backup/page.tsx
Normal file
13
frontend/app/accounting/settings/backup/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="پشتیبانگیری"
|
||||
module="settings"
|
||||
docType="backup"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/settings/company/page.tsx
Normal file
13
frontend/app/accounting/settings/company/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="اطلاعات شرکت"
|
||||
module="settings"
|
||||
docType="company"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/settings/general/page.tsx
Normal file
13
frontend/app/accounting/settings/general/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تنظیمات عمومی"
|
||||
module="settings"
|
||||
docType="general"
|
||||
/>
|
||||
);
|
||||
}
|
||||
13
frontend/app/accounting/settings/tax/page.tsx
Normal file
13
frontend/app/accounting/settings/tax/page.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { BusinessDocsPage } from "@/components/accounting/BusinessDocsPage";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<BusinessDocsPage
|
||||
title="تنظیمات مالیاتی"
|
||||
module="settings"
|
||||
docType="tax"
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -49,7 +49,7 @@ const STEP_LINKS = {
|
||||
has_base_currency: { href: "/accounting/currencies", label: "ارز پایه" },
|
||||
has_fiscal_year: { href: "/accounting/fiscal", label: "سال مالی" },
|
||||
has_fiscal_period: { href: "/accounting/fiscal", label: "دوره مالی" },
|
||||
has_cash_box: { href: "/accounting/treasury", label: "صندوق" },
|
||||
has_cash_box: { href: "/accounting/treasury/cash-boxes", label: "صندوق" },
|
||||
has_customer: { href: "/accounting/customers", label: "مشتری" },
|
||||
has_voucher: { href: "/accounting/vouchers", label: "سند حسابداری" },
|
||||
} as const;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user