--- title: Time Tracking & Project Billing (Pro addon) description: Log billable time against clients & projects, then convert tracked time into invoice line items with one click. Replaces standalone time-trackers for freelancers and agencies. ---
Pro · Professional plan Requires Easy Invoice Pro with a Professional (or Agency) license. Compare plans →
# Time Tracking & Project Billing A focused, freelancer-friendly addon for logging billable time and turning entries into invoice line items. No external integrations, no separate timer app — log time inside Easy Invoice, click a button, get an invoice. ## When to use it - You bill hourly for projects, retainers, or support - You currently keep time in a spreadsheet, Toggl, Harvest, or "in your head" - You want the invoice line item to include date + hours + rate automatically If you bill fixed-fee only, you probably don't need this addon — use the regular invoice builder instead. ## Enabling 1. Open **Easy Invoice → Addons** 2. Find **Time Tracking & Project Billing** 3. Click **Activate** 4. The "Settings →" link on the card takes you to the addon page: **Easy Invoice → (sidebar) → Time Tracking & Project Billing** (slug: `easy-invoice-addon-time-tracking`) On first activation the addon creates one custom table: ```sql {prefix}_easy_invoice_time_entries ``` Schema: | Column | Type | Notes | |---|---|---| | `id` | BIGINT PK | | | `client_id` | BIGINT | Nullable. References a WordPress user (Easy Invoice stores clients as users, not posts). | | `project` | VARCHAR(255) | Free text — your own project label. | | `description` | TEXT | What you did. | | `minutes` | INT | Round-to-nearest-minute (no float drift). | | `rate` | DECIMAL(10,2) | Hourly rate at the time of entry. | | `entry_date` | DATE | When the work was done. | | `billed_invoice_id` | BIGINT NULL | Set when entry is rolled into an invoice. | | `created_at`, `updated_at` | TIMESTAMP | | Indexed on `client_id`, `billed_invoice_id`, `entry_date`. Deactivating the addon **keeps** the table so you don't lose entries if you re-enable later. ## Quick start ### 1. Log time On the addon page, the **Log time** form takes: | Field | Required | What it does | |---|---|---| | **Client** | optional | Pick a WordPress user (the same list that powers the rest of Easy Invoice). | | **Project** | optional | Free text — e.g. "Website redesign", "Q3 retainer". | | **What did you do?** | **required** | Becomes the line-item description on the invoice. | | **Hours** | **required** | Fractional. Stored as `round(hours × 60)` minutes. | | **Hourly rate** | optional | If empty, the entry has zero monetary value (you can still use it for time reporting). | | **Date** | required (defaults to today) | When the work was done. | Submit. The entry appears in the table immediately and counts toward the hours/value tiles at the top. ### 2. Filter the list Two filters across the entries table: - **Client** — show only entries for one client - **Status** — `Unbilled` (default) / `Billed` / `All` The summary tiles (Entries / Hours / Value) reflect the **filter set**, not just the visible page — totals are computed via SQL `SUM()` across every matching row, so a 5,000-entry account still shows the right total. ### 3. Invoice from entries 1. Filter the list to the client you want to bill (and `Unbilled`). 2. Tick the checkboxes on the entries you want to invoice (the header checkbox selects all visible). 3. In the table header, choose the **Bill to…** client. 4. Click **Invoice selected**. The addon creates a **draft Easy Invoice** with: - One line item per time entry. Description includes the entry's date, hours, and rate. Quantity = hours; Unit price = rate. - Customer snapshot (name + email) copied from the WordPress user's profile. - Subtotal + total set to the sum of the entries' values (tax / adjustments applied later when you open the invoice). - Status = `draft` so it doesn't email anyone yet. - Sequence number assigned via the existing `InvoiceNumberService` so it respects your invoice-number prefix and counter. A success banner shows up with an **Open draft invoice →** link. The invoiced entries are immediately marked **Billed #{invoice_id}** in the entries list. ## Behaviour details ### Clients are WordPress users Easy Invoice stores clients as WP users with the `customer` role, not as a post type. The addon's client picker: 1. First tries `EasyInvoice\Repositories\ClientRepository::all()` (preferred — handles legacy data paths) 2. Falls back to `get_users(['role__not_in' => ['Administrator']])` if the repository class isn't available If your client picker is empty, see [Troubleshooting → empty client list](#empty-client-list). ### Dual-write of customer / client id When the addon creates an invoice from entries it writes **both** meta keys: ``` _easy_invoice_customer_id (canonical, used by InvoiceRepository) _easy_invoice_client_id (legacy, used by some AJAX listing endpoints) ``` This is a defensive choice because the rest of the plugin uses both keys inconsistently. Without it, the new invoice might be invisible to certain listing filters. ### Pagination The entries table is paginated at **50 / page** with proper `OFFSET`/`COUNT(*)` queries. The Hours and Value tiles always reflect the entire matching set (via SQL `SUM`), not just the current page. ## Permissions By default any user with `manage_options` (Administrator) can use the addon. If you also have the **[Team Members & Audit Log](./team-roles)** addon enabled, every action through Time Tracking is recorded in the audit log under the actions: - `time_entry_added` - `time_entry_deleted` - `time_tracking_invoice_created` ## Hooks for developers | Hook | Type | When | |---|---|---| | `easy_invoice_time_tracking_invoice_created` `(invoice_id, client_id, entries)` | action | Just after a draft invoice is created from selected entries. | Example — auto-mark the new invoice as Sent: ```php add_action('easy_invoice_time_tracking_invoice_created', function ($invoice_id, $client_id, $entries) { update_post_meta($invoice_id, '_easy_invoice_status', 'available'); }, 10, 3); ``` ## Troubleshooting ### Empty client list The addon shows users from the same source as the rest of Easy Invoice. If empty: 1. Confirm at least one user exists who isn't an Administrator (`Users → All Users`) 2. Confirm `EasyInvoice\Repositories\ClientRepository` is loadable — disable Easy Invoice Pro briefly to rule out a Pro side-effect ### "Invoice selected" returns to the form with no draft Check: 1. **Bill to…** is set (the button is disabled until a client is chosen) 2. The selected entries' rows actually have checkboxes ticked (only Unbilled rows have checkboxes; Billed rows show a green pill instead) 3. PHP error log — if `wp_insert_post` failed, the addon redirects with `tt_notice=create_failed` ### Hours look wrong after editing The addon doesn't currently support inline edit — delete the entry and log it again, or use SQL to update the row. Editing is on the short-list for the next addon iteration. ## Roadmap Features deliberately deferred from the initial release: - **Inline edit** (today: delete + re-create) - **CSV import** from Toggl / Harvest (today: manual entry) - **Per-task hourly rates** (today: per-entry only) - **Built-in start/stop timer** (today: manual hours field) - **Auto-link recurring time entries to a recurring invoice** If you want one of these prioritised, [open an issue](https://github.com/mantrabrain/easy-invoice-docs/issues) or [contact support](../support). ## See also - [Invoices walkthrough](../invoices) - [Smart Reminders & Late Fees](./smart-reminders) — pair with Dunning to chase those generated invoices - [Team Members & Audit Log](./team-roles) — track which staff member logged which time - [Addons overview](./)