# Technical documentation — quickbooks_integration

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

Credential values that appear in source are not copied here. Host names, database names, URL paths, and setting names are.

## Architecture

PHP on Apache (rewrite + headers in `.htaccess`), two QuickBooks stacks, MySQL queue.

```text
quickbooks_integration/
├── qbwc.php / qbwcEIT.php / qbwcTS.php     ConsoliBYTE SOAP (Desktop)
├── handler.php / handlerEIT.php / handlerTS.php
├── functions.php / functionsEIT.php / functionsTS.php
├── quote.connection.php / eit.connection.php / ts.connection.php
├── config.php                              gitignored; backups in git
├── QuickBooks.php                          ConsoliBYTE DevKit (EPL)
├── AddClientService/                       second DevKit copy + CustomerAdd
├── auth.php, invoice.php, index.php, create.php, create.js   QBO
├── composer.json                           quickbooks/v3-php-sdk ^5.3
├── vendor/quickbooks/v3-php-sdk            5.3.5
├── *.qwc                                   Web Connector apps
├── cron.hourly.php, credited.php
├── XMLOutput/, www_logs/, php-error-*.log
└── WebConnector/, images/, example docs
```

There is no framework (no Laravel/CI4), no DI, no queue product other than ConsoliBYTE `quickbooks_queue`.

## Languages and frameworks

| Piece | Evidence |
|---|---|
| PHP 5.x-era mysqli + leftover mysql_* | Source |
| PHP >= 5.6 for v3 SDK | `composer.lock` require |
| JavaScript / jQuery 3.4.1 CDN | `create.php` |
| Apache `mod_rewrite` | `.htaccess` |
| Maven POM (upstream kit packaging) | `AddClientService/pom.xml` — Intuit `quickbooks-php-devkit` 1.0.0-SNAPSHOT |
| qbXML 2.0 | InvoiceAdd documents |
| SOAP | builtin ConsoliBYTE server, not PHP `ext/soap` |

## Databases

| Catalog | Host in git | Used for |
|---|---|---|
| `quickbook_webservice` | `sql27.jnb1.host-h.net` (live strings); `127.0.0.1` in `auth.php`; `localhost` `quickbooks_webservices` in config backups | Queue, `invoice_table`, `customer_table`, `tmpQBImport`, `quickbooks_request` |
| `quoteslive` | `sql24.jnb1.host-h.net` | Quotes header/lines/status |
| `eitlivedb` | `sql24.jnb1.host-h.net` | EIT clients, billing, SLA |

Users named in source (passwords omitted): `quickbks_dbu`, `dbu_quotes`, `dbu_billing`, `root`, `debian-sys-maint`.

Tables referenced (no CREATE for the live shape except the stub in `config.php.bak`):

- `invoice_table` — connector work item
- `customer_table` — QB ListID for import customers
- ConsoliBYTE `quickbooks_queue` and related (created by `QuickBooks_Utilities::initialize`)
- `quickbooks_request` — Online pending payload
- `tmpQBImport` — Timesheet staging
- Quotes: `tblQuote`, `tblQuoteItem`, `tblClient`, `tblProduct`
- EIT: `tblClient`, `tblBilling`, `tblBillingContact`, `tblContact`, `tblSLA`, `tblType`, `tblUser`, `tblCodeFault`, `tblBillingCode`, `vBillingCode`, `tmpBilling`

## APIs

### HTTP (this app)

| Rewrite / file | Method | Role |
|---|---|---|
| `/auth/` → `auth.php` | GET/POST | OAuth2 + optional stream persist |
| `/invoice/` → `invoice.php` | GET/POST | QBO invoice + redirect |
| `/create/` → `create.php` | GET | Demo UI |
| `/handlerEIT/` → `handlerEIT.php` | GET | EIT enqueue |
| `handler.php`, `handlerTS.php`, `credited.php`, `cron.hourly.php` | GET | No rewrite |

CORS: `Access-Control-Allow-Origin: http://eit.overdrive.co.za`; methods POST, GET, OPTIONS, DELETE, PUT.

### SOAP (Desktop)

WSDL from ConsoliBYTE (`QUICKBOOKS_WSDL`). Endpoints in QWC:

| AppName | URL | Interval |
|---|---|---|
| Import Quotes | `https://qb.overdrive.co.za/qbwc.php` | 30 min |
| Import Billing | `https://qb.overdrive.co.za/qbwcEIT.php` | 10 min |
| Import Software Timesheet | `https://qb.overdrive.co.za/qbwcTS.php` | 10 min |
| Quotes/Billing/Timesheet Add Customer | `https://qb.overdrive.co.za/AddClientService/qbwc*.php` | see those QWC files |

QBType: **QBFS**. AppSupport: `https://qb.overdrive.co.za/support.php`.

Mapped actions: `QUICKBOOKS_ADD_INVOICE`, `QUICKBOOKS_QUERY_INVOICE`; AddClientService: `QUICKBOOKS_ADD_CUSTOMER`.

### REST (Online)

- Auth: Intuit OAuth2 via SDK `DataService`.
- Query: `GET .../v3/company/{id}/query?query=...&minorversion=47`
- Invoice: `POST .../v3/company/{id}/invoice?minorversion=47`
- CompanyInfo helper (unused at runtime): minorversion 12
- Base: `https://sandbox-quickbooks.api.intuit.com/v3/company/`
- Redirect URI: `https://qb.overdrive.co.za/auth/`
- SDK log dir: `/var/log/quickbooks`

## External integrations

| System | Direction | Trigger | Data |
|---|---|---|---|
| QuickBooks Desktop | Out (qbXML) | Web Connector poll | InvoiceAdd, InvoiceQuery, CustomerAdd |
| QuickBooks Online sandbox | Out (HTTPS) | After OAuth | Invoice JSON |
| Intuit developer app `eit` | Auth | `/auth/` | OAuth code/token |
| Quotes (`quoteslive`) | In/out SQL | handler + SOAP callbacks | Quote lines, status |
| EIT (`eitlivedb`) | In SQL | handlerEIT + SOAP | Billing rows |
| Timesheet | In SQL via `tmpQBImport` | handlerTS | Memo, qty, rate |
| Workstation Web Connector 2.0 | Client | Scheduled | SOAP to AppURL |

Failure behaviour: ConsoliBYTE `errmap` `*` writes `quickbooks_errnum` / `quickbooks_errmsg` on `invoice_table`. Online failures: JSON Fault/Fatal or redirect with payload. No retry worker except hourly `n`→`q`.

## Auth

- Web Connector: ConsoliBYTE user stored in MySQL (created by `createUser`).
- QBO: OAuth2 client id/secret in `auth.php`; session `accessToken`, `accessTokenExpire`.
- HTTP handlers: none.

## Background work

- Intuit Web Connector scheduler (QWC `RunEveryNMinutes`).
- `cron.hourly.php` — host crontab **Unknown**.
- `sleep(2)` / `sleep(4)` inside SOAP request builders.

No Redis, no dedicated job runner.

## Storage and logs

- `XMLOutput/xmlOuputEit_*.txt`, `xmlOuputTS_*.txt`, Quotes equivalents (`date(YmdHis)` used **without quotes** in `fopen` — PHP constant fallback; **Inferred** filenames are not the intended timestamp).
- `php-error-eit-func.log`, `php-error-ts.log`, `php-error-quotes.log` (quoted in functions.php).
- SDK: `/var/log/quickbooks`.
- `www_logs/` present in tree.

## Environment configuration

| File | Notes |
|---|---|
| `.gitignore` | ignores `.htaccess`, `config.php`, `QuickBooks`, `test.php` — yet `.htaccess` and `QuickBooks.php` are tracked |
| `config.php.bak` | timezone Africa/Johannesburg; DSN localhost |
| `config.old.php` | timezone Europe/Minsk |
| `serverconfig*.qwc` | production-looking `qb.overdrive.co.za` vs old LAN URLs |

`.env` is not used.

## Security (observed)

- Secrets in git (OAuth, DB). Rotate is **Requires Confirmation** outside this task.
- SQL built by string concat (quote numbers, client ids, XML). Quotes handler uses `mysqli_real_escape_string` on some fields; many EIT/TS queries interpolate IDs.
- `display_errors` on some public scripts.
- CORS restricted to one origin for EIT.

## Development and testing

- No PHPUnit project tests. SDK has phpunit as require-dev of the vendor package only.
- `dev_qbwc_tester.php` — local SOAP tester.
- ConsoliBYTE `AddClientService/docs/example_*.php` and `dev/` — upstream examples.
- `create.php` / `create.js` — browser demo for Online.
- CI (Bitbucket Pipelines, Sonar): **not in this repository**.
- Docker: **not present**.

## Deployment

**Documented** (`README.txt`): copy files to web root (historically `EIT/www/quickbooks_webservices`), SSL, load QWC into Web Connector, QuickBooks admin + "always allow access even if QuickBooks is not running", single-user to set preferences.

Composer install is required for Online (`vendor/autoload.php` in `auth.php`). Desktop path uses `QuickBooks.php` in-repo, not Composer.

Live host **Inferred** as `qb.overdrive.co.za` from QWC and OAuth redirect. Confirmation of current deploy **Unknown**.
