Integration with bookkeeping system (SZ-78)
Artur Hefczyc opened 4 months ago

There are a few features I would like to have to avoid work duplication:

  1. Syncing time records
  2. Generating invoices (contractor for us and us for our customers)

This is probably more distant work and we would need to have some API or REST API. Not sure what would be the best way to approach. Every bookkeeping system has different APIs. We now use QBO, but maybe there is a way to prepare it in more generic way to allow for easier integration with other CPA systems.

  • rk@tigase.net referenced from other issue 2 months ago
  • rk@tigase.net referenced from other issue 2 months ago
  • rk@tigase.net referenced from other issue 1 month ago
  • rk@tigase.net commented 1 month ago

    Phase 1 — Billing Statement Export (near-term) Sztab already has time tracking (SZ-71). The missing piece is a billing rate per user per project, which is a small schema addition. With that in place, Sztab can generate a monthly billing statement: grouped by calendar month, with hours X rate and subtotals - exportable as PDF or CSV. This is the "copy-paste into your invoicing system" path, and it's buildable quickly.

    Phase 2 — Native Invoice Entity (medium-term) Sztab generates and stores invoices directly: line items pulled from time entries, a Draft => Sent => Paid status lifecycle, PDF rendering, and a shareable link. This makes Sztab the source of truth for contractor-to-Tigase billing - which is exactly our use case.

    Phase 3 — QBO Sync (longer-term) As discussed separately, a provider-agnostic bookkeeping integration layer (QBO first, extensible to Xero etc.) would push finalized invoices out to the accounting system automatically. No double-entry.

    Starting with Phase 1 as a near-term addition: it has immediate practical value and the groundwork is already there.

  • rk@tigase.net commented 2 weeks ago
    TaskEstimate
    Flyway migration1–2h
    BillingStatementService3–4h
    BillingStatementController (JSON + CSV)2h
    PDF rendering (server-side)4–6h
    Multi-project consolidated statement3–4h
    Frontend — rate field on membership UI2h
    Frontend — billing statement page4–5h
    Frontend — download wiring1–2h
    Total~20–26h
  • rk@tigase.net commented 2 weeks ago
    rksuma@Ramakrishnans-MacBook-Pro sztab % git checkout -b feature/SZ-78-Integration-with-bookkeeping-system
    Switched to a new branch 'feature/SZ-78-Integration-with-bookkeeping-system'
    rksuma@Ramakrishnans-MacBook-Pro sztab %
    
  • rk@tigase.net changed state to 'In Progress' 2 weeks ago
    Previous Value Current Value
    Open
    In Progress
  • rk@tigase.net commented 1 week ago

    Implementation notes (Phase 1)

    repository layer: monthlyBilling() in TimeEntryRepository does one JPQL query, one row per (project, year, month). has userId, username, projectId, projectName, year, month, totalMinutes. billing rate is NOT in this query - it lives in project_memberships and dragging it into a GROUP BY is more trouble than its worth.

    service layer (BillingStatementServiceImpl.generate):

    • resolve project scope: caller passes projectIds or we fall back to findActiveProjectsForUser
    • fire monthlyBilling() with resolved list
    • for each row: single findActiveMembership call (not two - rate and currency both come from the same membership lookup), compute amount = (totalMinutes / 60) * rate, build BillingLineItem

    wrap it all in BillingStatement with grandTotal.

    notes:

    • billing rate/currency on project_memberships, per user per project, set by owner/maintainer
    • no rate configured = amount is zero, currency defaults USD
  • rk@tigase.net commented 1 week ago

    Implementation Sequence:

    This is the sequence:

    1. Phase I: Create billing statement — computed from time entries × rate; this is not stored
    2. Implement invoice lifecycle: Native invoice entity - generate from billing statement, store it, Implement lifecycle: Draft => Sent => Paid lifecycle, Made visible through REST using shareable token
    3. Phase 2: Invoice export — render stored invoice as PDF (renderer registry, pluggable formats)
    4. Phase 3: QBO Sync
  • rk@tigase.net commented 1 week ago

    Phase I Complete: Sztab can now compute a monthly billing statement from logged time entries. Each project membership carries a billing rate (hourly, per user). Sztab uses these two to produce a breakdown of hours worked times rate, grouped by month and project. This can be exported as PDF or CSV and used as the basis for preparing an invoice in any external system.

    Phase 1 — Billing statement (report, not persisted)

    • V25: added billing_rate_per_hour, billing_currency to project_members
    • ProjectMember entity + DTO updated with billing fields
    • UpdateProjectMemberDto extended with optional billing fields
    • ProjectMemberService.updateMember signature extended to carry billing fields
    • TimeEntryRepository.monthlyBilling() — new JPQL query, one row per (project, year, month)
    • BillingStatement + BillingLineItem DTOs
    • BillingStatementService + impl — resolves project scope, joins billing rate from membership, computes hours x rate per line item (single membership lookup per row)
    • BillingStatementControllerGET /api/projects/{projectId}/billing/statement
    • ConsolidatedBillingControllerGET /api/billing/statement/consolidated (no project scope, spans all user memberships)
  • rk@tigase.net commented 1 week ago

    Invoice Lifecycle defined and implemented:

    InvoiceStatus enum: DRAFT, SENT, PAID
    Invoice entity: status, sent_at, paid_at fields
    InvoiceService.markSent: transitions DRAFT => SENT, records sentAt
    InvoiceService.markPaid: transitions SENT => PAID, records paidAt
    InvoiceController: PATCH /api/invoices/{id}/sent, PATCH /api/invoices/{id}/paid
    
  • rk@tigase.net commented 1 week ago

    UI for billing and invoicing:

    Frontend additions

    1. two type files (billing.ts, invoice.ts)
    2. two API modules (billing.ts, invoices.ts),
    3. and two pages (InvoiceListPage, InvoiceDetailPage).

    Existing files modified:

    • App.tsx (routes),
    • Sidebar.tsx (nav item),
    • ProjectMembersPage.tsx (billing rate field on member edit).
  • rk@tigase.net commented 1 week ago

    being integration tested

  • rk@tigase.net changed state to 'In QA' 1 week ago
    Previous Value Current Value
    In Progress
    In QA
  • rk@tigase.net commented 1 week ago
    rksuma@Ramakrishnans-MacBook-Pro sztab % git add pom.xml backend/pom.xml query-cli/pom.xml query-dsl/pom.xml
    git commit -m "SZ-78: merge to wolnosc, resolve pom version conflicts"
    git push origin wolnosc
    [wolnosc b48e963] SZ-78: merge to wolnosc, resolve pom version conflicts
    Enumerating objects: 4, done.
    Counting objects: 100% (4/4), done.
    Delta compression using up to 12 threads
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (2/2), 337 bytes | 337.00 KiB/s, done.
    Total 2 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
    To https://tigase.dev/sztab.git
       8fe3015..b48e963  wolnosc -> wolnosc
    rksuma@Ramakrishnans-MacBook-Pro sztab % 
    
  • rk@tigase.net changed state to 'Closed' 1 week ago
    Previous Value Current Value
    In QA
    Closed
  • rk@tigase.net referenced from other issue 1 week ago
  • rk@tigase.net commented 1 week ago
issue 1 of 1
Type
New Feature
Priority
Normal
Assignee
Version
none
Sprints
n/a
Customer
n/a
Issue Votes (0)
Watchers (2)
Reference
SZ-78
Please wait...
Page is in error, reload to recover