# Business rules — quickbooks_integration

Evidence date: 2026-10-05. Source: `master` at `4ff6e81` (2020-04-30).

No business-rules document exists in the repository. Rules below are taken from PHP conditions, SQL, QWC XML, and comments. Each rule is **Implemented**, **Inferred**, or **Documented**. Comments are not signed-off policy.

Credential values are omitted.

## Web Connector and queue

### BR-001 — Web Connector user is username/password

**Documented** (`README.txt`) and **Implemented** in `config.php.bak`: `$qbwc_user = 'username'`, `$qbwc_pass = 'password'`. Operators are told to type that password into Web Connector.

### BR-002 — Uninitialized DSN creates schema and the invoice_table stub

**Implemented** in `config.php.bak`. Creates ConsoliBYTE tables plus a MyISAM `invoice_table` with id, reference, listid, editsequence, errors, invoice_number, paid, strQuoteNumber. Live inserts also use `strApplication`, `refClientID`, `strPeriod`, memos, qty/rate — columns not in this CREATE. **Inferred:** production schema drifted; the CREATE is bootstrap only.

### BR-003 — Concurrent and "really fast" SOAP logins are allowed

**Implemented.** `deny_concurrent_logins` false, `deny_reallyfast_logins` false.

### BR-004 — Queue rows with status `n` are reset to `q` hourly

**Implemented** (`cron.hourly.php`). Comment intent: errors / unpaid email — **not coded**.

### BR-005 — Each SOAP stack ignores other applications' queue rows

**Implemented.** Quotes handler returns `QUICKBOOKS_NOOP` unless `strApplication == "Quotes"`. EIT requires `"EIT"`. TS requires `"TS"`.

### BR-006 — Sleep before returning qbXML

**Implemented.** Quotes and TS sleep 2 seconds; EIT sleeps 4. Comment: Web Connector goes too fast otherwise.

## Quotes

### BR-007 — All Quotes invoices use one QuickBooks customer

**Implemented.** Live: `customer_table.id = 1` ("A Import Quotes"). Lamp id 4 is commented. Bill address line 1 is the Quotes client name, not a per-client QB customer.

### BR-008 — Line item SKU is `tblProduct.strSKU`; labour is no longer remapped

**Implemented.** 2016 comment: labour codes now included in QB. `blnLabour` is still selected.

### BR-009 — Tax code on every Quotes line is `S`

**Implemented.** Comment maps S = Standard VAT, Z = no VAT, E = exempt. Only S is emitted.

### BR-010 — CDATA must not contain `]]>`

**Implemented.** Description `str_replace("]]>", "", ...)`.

### BR-011 — After add, Quotes status becomes `4. Invoiced`

**Implemented.** `insert_invoice_number` on `quoteslive.tblQuote`.

### BR-012 — When QuickBooks reports paid, Quotes status becomes `5. Paid`

**Implemented** as intended in `paid_invoices`. The generated SQL is not a valid multi-invoice UPDATE. Treat paid-sync as **Requires Confirmation**.

### BR-013 — Credited quotes drop unpaid connector rows

**Implemented.** Delete `invoice_table` where quote number matches and `paid = 0`, then Quotes status `6. Credited`.

### BR-014 — Duplicate quote enqueue is unchecked

**Implemented** as absence. TODO in `AddClientService/handler.php` is not in root `handler.php`.

## EIT billing

### BR-015 — Month-end clients are those with billable work in the calendar month, invoice field empty, not Silicon Overdrive, not Fixed SLA type

**Implemented** (`get_monthend_client_details`). Billable means `vBillingCode.blnBill = '1'` OR `strCode = 'Labour NC'`. Empty invoice: `LENGTH(tblBilling.strInvoice) < 2`.

### BR-016 — Non-SLA lines are `refSLAID = 0` with the same billable/empty-invoice/date filters

**Implemented** (`get_nonSLA_client_billing_items`).

### BR-017 — Quantity is multiplier × unit; rate is billing rate

**Implemented.**

### BR-018 — INTERNET division is billed as HARDWARE for department branching

**Implemented.** Then item name is `software` if department is software, else `support - {first initial}{surname initial}`.

### BR-019 — SLA excess hours use rollover rate as a second line

**Implemented** when rollover flag is set. `$dblSupportHoursLeft` is used before assignment. **Requires Confirmation.**

### BR-020 — Transit: one charge per distinct on-site date, item `Transit Fixed`, rate client `dblBillableTransit`

**Implemented** (`count_transit_charges_client_billing_items` + invoice line). Location values: `ON-SITE`, `ON-SITE and OFF-SITE`.

### BR-021 — Client names have apostrophes stripped before insert into `invoice_table.reference`

**Implemented** (`cleanClientName`).

### BR-022 — EIT invoices use QB customer id 2

**Implemented.** Comment: "A Import Billing". Lamp id 3 commented.

### BR-023 — Bill address is first 40 characters of client name

**Implemented.**

## Timesheet

### BR-024 — Staging table `tmpQBImport` is the only TS intake; it is truncated after enqueue

**Implemented.**

### BR-025 — TS invoices use QB customer id 5 and item `Software`

**Implemented.** Memo on the invoice header: `SDev {Mon}`.

### BR-026 — Sibling TS queue rows for the same client that are still `q` are folded into one qbXML and marked `s`

**Implemented.** Comment: do not duplicate invoices.

## QuickBooks Online

### BR-027 — Accounting scope only

**Documented / Implemented.** Scope `com.intuit.quickbooks.accounting`.

### BR-028 — OAuth environment is Intuit development / sandbox API

**Implemented.** `baseUrl => 'development'`; REST host `sandbox-quickbooks.api.intuit.com`.

### BR-029 — Access token lifetime treated as 3600 seconds

**Implemented** in `index.php` only. `invoice.php` does not check `accessTokenExpire`.

### BR-030 — Online invoice currency is ZAR

**Implemented.** Hard-coded `CurrencyRef` ZAR.

### BR-031 — Customer and item must already exist in QBO (lookup by display name / item name)

**Implemented** in `invoice.php` (customer DisplayName, item Name). Missing QueryResponse is not handled; would error at `...[0]->Id`.

### BR-032 — `quickbooks_request` holds a single pending Online payload

**Implemented.** `TRUNCATE` then insert one row. Column name `reguest_bill_id` (typo) is what the INSERT uses.

## Access and security (as coded)

### BR-033 — HTTP enqueue endpoints have no application authentication in this repo

**Implemented** as absence. Anyone who can hit the PHP URLs with query params can enqueue. **Inferred** that network placement was the control. **Requires Confirmation.**

### BR-034 — Secrets and DSNs are in source

**Implemented.** OAuth client id/secret, MySQL passwords, and company id are committed in PHP. This is a fact of the tree, not a recommended policy.

### BR-035 — Display errors

**Implemented.** Several public scripts set `display_errors` on (`invoice.php`, `index.php`, `handlerEIT.php`, `cron.hourly.php`). SOAP servers set display_errors off.
