qbwc-kit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qbwc_kit-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eren Altuntas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT Eren AltuntasS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: qbwc-kit
3
+ Version: 0.1.0
4
+ Summary: Talk to QuickBooks Desktop over the Web Connector: SOAP callbacks, qbXML, and test doubles.
5
+ Author: Eren Altuntas
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/meister5/qbwc-kit
8
+ Project-URL: Issues, https://github.com/meister5/qbwc-kit/issues
9
+ Keywords: quickbooks,qbxml,web-connector,qbwc,soap,accounting,erp
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: server
23
+ Requires-Dist: fastapi>=0.100; extra == "server"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7; extra == "dev"
26
+ Requires-Dist: fastapi>=0.100; extra == "dev"
27
+ Requires-Dist: httpx>=0.24; extra == "dev"
28
+ Requires-Dist: ruff>=0.4; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # qbwc-kit
32
+
33
+ [![CI](https://github.com/meister5/qbwc-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/meister5/qbwc-kit/actions/workflows/ci.yml)
34
+ [![Python](https://img.shields.io/badge/python-3.10%20%E2%80%93%203.13-blue)](pyproject.toml)
35
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
36
+
37
+ QuickBooks Desktop has no HTTP API. The only supported way in is the **Web Connector**: a
38
+ Windows service that polls *your* SOAP endpoint on a schedule, asks it for qbXML, hands that
39
+ to QuickBooks over COM, and posts the response back.
40
+
41
+ So before you can read a single invoice you have to implement eight SOAP callbacks, serve a
42
+ WSDL, keep a ticket-based session alive across many HTTP round trips, and hand-build XML that
43
+ QuickBooks rejects with unhelpful errors when the element order is wrong. This library is all
44
+ of that, so the only thing left to write is the part that is actually about your data.
45
+
46
+ ```python
47
+ from qbwc_kit import QBWCService, StaticAuthenticator, qbxml
48
+ from qbwc_kit.qbxml import QBXMLRequest
49
+ from qbwc_kit.server import create_app
50
+
51
+
52
+ class SyncCustomers:
53
+ name = "customers"
54
+
55
+ def run(self, ctx):
56
+ request = qbxml.query("Customer", max_returned=100, iterator="Start")
57
+ while True:
58
+ result = yield QBXMLRequest([request])
59
+ page = result.first().raise_for_status()
60
+ save(page.records)
61
+ if not page.has_more:
62
+ return
63
+ request.iterator = "Continue"
64
+ request.iterator_id = page.iterator_id
65
+
66
+
67
+ service = QBWCService(
68
+ authenticator=StaticAuthenticator("qbwc", "s3cret", [SyncCustomers()])
69
+ )
70
+ app = create_app(service, endpoint_url="https://books.example.com/qbwc")
71
+ ```
72
+
73
+ That `while` loop is the point. Each `yield` suspends the task until the Web Connector comes
74
+ back with the response, so pagination, conditional writes, and read-then-write jobs stay in
75
+ ordinary control flow instead of being flattened into a per-request state machine.
76
+
77
+ ## Install
78
+
79
+ Not on PyPI yet — install from the repository:
80
+
81
+ ```bash
82
+ pip install git+https://github.com/meister5/qbwc-kit # core: standard library only
83
+ pip install 'qbwc-kit[server] @ git+https://github.com/meister5/qbwc-kit' # adds the FastAPI adapter
84
+ ```
85
+
86
+ Or clone it and `pip install -e '.[dev]'` to run the tests.
87
+
88
+ The core — SOAP, qbXML, sessions, the WSDL generator — has no dependencies outside the
89
+ standard library. FastAPI is only needed if you use `qbwc_kit.server`; the service itself is
90
+ just `dispatch(soap_body) -> soap_body`, so it drops into Flask, Django, or bare WSGI without
91
+ this package caring.
92
+
93
+ ## Testing without QuickBooks
94
+
95
+ The awkward part of a Web Connector integration is that exercising it normally requires a
96
+ Windows box, a QuickBooks Desktop install, an open company file, and a human clicking *Update
97
+ Selected*. Which means the failure modes that matter — a task that never terminates, an
98
+ iterator that loops forever, a non-zero status nobody checked — are exactly the ones you only
99
+ find in production.
100
+
101
+ `qbwc_kit.testing` replaces both ends:
102
+
103
+ ```python
104
+ from qbwc_kit.testing import FakeQuickBooks, FakeWebConnector, service_transport
105
+
106
+ connector = FakeWebConnector(transport=service_transport(service),
107
+ username="qbwc", password="s3cret")
108
+ quickbooks = FakeQuickBooks(entities={"Customer": [{"ListID": "1", "Name": "Acme"}]})
109
+
110
+ result = connector.run_update(quickbooks)
111
+
112
+ assert result.progress[-1] == 100
113
+ assert quickbooks.seen[0].count("iterator=\"Start\"") == 1
114
+ ```
115
+
116
+ `FakeWebConnector` replays the real call sequence — `clientVersion`, `authenticate`, the
117
+ `sendRequestXML`/`receiveResponseXML` loop, `closeConnection` — and raises if the session
118
+ doesn't terminate, which catches runaway iterators in milliseconds instead of in the
119
+ connector's log. `FakeQuickBooks` answers qbXML the way QuickBooks does: paged iterators,
120
+ `MaxReturned`, status 1 for an empty result, status 3100 for an unsupported request.
121
+
122
+ ## The parts that bite
123
+
124
+ **Status codes ride on successful envelopes.** A request QuickBooks refused (status 3100,
125
+ "not available in this edition") comes back as a perfectly well-formed response document with
126
+ no records in it. If you parse for rows and ignore the status, an unsupported request is
127
+ indistinguishable from an empty table, and a cache built on top of it degrades into empty
128
+ results without anything logging an error. Every `Response` here carries its status, `ok`
129
+ distinguishes "nothing found" (status 1, genuinely fine) from "never ran", and
130
+ `raise_for_status()` is one call away.
131
+
132
+ **Element order is part of the schema.** qbXML is a sequence, not a bag. `MaxReturned` before
133
+ the filters, `EditSequence` before the fields being changed. The builders emit the right order
134
+ so you pass a dict and stop thinking about it.
135
+
136
+ **Writes use optimistic concurrency.** Every `Mod` request must carry the `EditSequence` from
137
+ the last read, and a stale one is rejected rather than silently clobbering another user's
138
+ edit. `qbxml.mod()` requires it as a keyword argument for that reason.
139
+
140
+ **Iterators only exist on some entities.** Asking for one on an entity that doesn't support it
141
+ is an opaque parse error from QuickBooks, so `qbwc-kit` raises at build time instead.
142
+
143
+ **Returning 100 ends the session.** `receiveResponseXML` returns percent complete, and a
144
+ progress calculation that rounds up too early silently truncates the sync. `Session.progress()`
145
+ caps at 99 until the work is genuinely done.
146
+
147
+ **An unknown ticket is normal.** Restart the server mid-update and the next callback arrives
148
+ with a ticket that no longer exists. Faulting makes the Web Connector retry forever; this
149
+ service tells it the session is over instead.
150
+
151
+ ## Layout
152
+
153
+ | Module | What it does |
154
+ | --- | --- |
155
+ | `qbwc_kit.soap` | The small SOAP 1.1 slice QBWC actually uses — parse a call, build a response or fault |
156
+ | `qbwc_kit.qbxml` | Request builders (`query`, `add`, `mod`) and a status-aware response parser |
157
+ | `qbwc_kit.session` | Generator-based tasks, the request/response loop, ticket store with TTL |
158
+ | `qbwc_kit.service` | The eight callbacks, framework-agnostic |
159
+ | `qbwc_kit.wsdl` | WSDL generation, plus the `.qwc` file users import into the connector |
160
+ | `qbwc_kit.server` | Optional FastAPI adapter |
161
+ | `qbwc_kit.testing` | `FakeWebConnector`, `FakeQuickBooks` |
162
+
163
+ ## Example
164
+
165
+ [`examples/sync_to_sqlite.py`](examples/sync_to_sqlite.py) is a complete integration: it
166
+ mirrors customers and invoices into SQLite, syncs incrementally off a stored watermark, and
167
+ generates the `.qwc` file to import into the Web Connector.
168
+
169
+ ```bash
170
+ python examples/sync_to_sqlite.py --write-qwc mirror.qwc # generate the connector file
171
+ python examples/sync_to_sqlite.py --url https://books.example.com/qbwc
172
+ ```
173
+
174
+ Two details in there are worth stealing. The watermark only advances after every page has been
175
+ written, so an interrupted run repeats work instead of skipping it. And it is rewound by a
176
+ minute, because QuickBooks stamps `TimeModified` from the workstation clock — a record saved
177
+ during the sync can otherwise land just behind the watermark and never be picked up again.
178
+
179
+ The example is covered by the test suite, so it can't drift from the library.
180
+
181
+ ## Deployment notes
182
+
183
+ - QBWC will not accept an endpoint on plain HTTP unless it is `localhost`. Use TLS.
184
+ - `soap:address` in the WSDL has to be the URL the connector actually posts to. Behind a
185
+ reverse proxy, that is the public URL, not `http://localhost:8000`.
186
+ - `OwnerID` and `FileID` in the `.qwc` file identify your integration to QuickBooks.
187
+ Generate them once and keep them; changing them forces every user to re-authorise.
188
+ - The connector authenticates with a password stored in Windows' credential store. Treat the
189
+ `authenticate` callback as a real auth boundary — `StaticAuthenticator` compares with
190
+ `secrets.compare_digest`, and anything you write should too.
191
+
192
+ ## Scope
193
+
194
+ Read and write access to list and transaction entities through qbXML, which is what the Web
195
+ Connector exposes. Not covered: QuickBooks Online (that has a real REST API — use it),
196
+ qbposXML for Point of Sale, and the direct COM `QBFC` interface, which needs code running on
197
+ the same Windows machine as QuickBooks.
198
+
199
+ ## Development
200
+
201
+ ```bash
202
+ pip install -e '.[dev]'
203
+ pytest -q
204
+ ruff check . && ruff format --check .
205
+ ```
206
+
207
+ ## License
208
+
209
+ MIT
@@ -0,0 +1,179 @@
1
+ # qbwc-kit
2
+
3
+ [![CI](https://github.com/meister5/qbwc-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/meister5/qbwc-kit/actions/workflows/ci.yml)
4
+ [![Python](https://img.shields.io/badge/python-3.10%20%E2%80%93%203.13-blue)](pyproject.toml)
5
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6
+
7
+ QuickBooks Desktop has no HTTP API. The only supported way in is the **Web Connector**: a
8
+ Windows service that polls *your* SOAP endpoint on a schedule, asks it for qbXML, hands that
9
+ to QuickBooks over COM, and posts the response back.
10
+
11
+ So before you can read a single invoice you have to implement eight SOAP callbacks, serve a
12
+ WSDL, keep a ticket-based session alive across many HTTP round trips, and hand-build XML that
13
+ QuickBooks rejects with unhelpful errors when the element order is wrong. This library is all
14
+ of that, so the only thing left to write is the part that is actually about your data.
15
+
16
+ ```python
17
+ from qbwc_kit import QBWCService, StaticAuthenticator, qbxml
18
+ from qbwc_kit.qbxml import QBXMLRequest
19
+ from qbwc_kit.server import create_app
20
+
21
+
22
+ class SyncCustomers:
23
+ name = "customers"
24
+
25
+ def run(self, ctx):
26
+ request = qbxml.query("Customer", max_returned=100, iterator="Start")
27
+ while True:
28
+ result = yield QBXMLRequest([request])
29
+ page = result.first().raise_for_status()
30
+ save(page.records)
31
+ if not page.has_more:
32
+ return
33
+ request.iterator = "Continue"
34
+ request.iterator_id = page.iterator_id
35
+
36
+
37
+ service = QBWCService(
38
+ authenticator=StaticAuthenticator("qbwc", "s3cret", [SyncCustomers()])
39
+ )
40
+ app = create_app(service, endpoint_url="https://books.example.com/qbwc")
41
+ ```
42
+
43
+ That `while` loop is the point. Each `yield` suspends the task until the Web Connector comes
44
+ back with the response, so pagination, conditional writes, and read-then-write jobs stay in
45
+ ordinary control flow instead of being flattened into a per-request state machine.
46
+
47
+ ## Install
48
+
49
+ Not on PyPI yet — install from the repository:
50
+
51
+ ```bash
52
+ pip install git+https://github.com/meister5/qbwc-kit # core: standard library only
53
+ pip install 'qbwc-kit[server] @ git+https://github.com/meister5/qbwc-kit' # adds the FastAPI adapter
54
+ ```
55
+
56
+ Or clone it and `pip install -e '.[dev]'` to run the tests.
57
+
58
+ The core — SOAP, qbXML, sessions, the WSDL generator — has no dependencies outside the
59
+ standard library. FastAPI is only needed if you use `qbwc_kit.server`; the service itself is
60
+ just `dispatch(soap_body) -> soap_body`, so it drops into Flask, Django, or bare WSGI without
61
+ this package caring.
62
+
63
+ ## Testing without QuickBooks
64
+
65
+ The awkward part of a Web Connector integration is that exercising it normally requires a
66
+ Windows box, a QuickBooks Desktop install, an open company file, and a human clicking *Update
67
+ Selected*. Which means the failure modes that matter — a task that never terminates, an
68
+ iterator that loops forever, a non-zero status nobody checked — are exactly the ones you only
69
+ find in production.
70
+
71
+ `qbwc_kit.testing` replaces both ends:
72
+
73
+ ```python
74
+ from qbwc_kit.testing import FakeQuickBooks, FakeWebConnector, service_transport
75
+
76
+ connector = FakeWebConnector(transport=service_transport(service),
77
+ username="qbwc", password="s3cret")
78
+ quickbooks = FakeQuickBooks(entities={"Customer": [{"ListID": "1", "Name": "Acme"}]})
79
+
80
+ result = connector.run_update(quickbooks)
81
+
82
+ assert result.progress[-1] == 100
83
+ assert quickbooks.seen[0].count("iterator=\"Start\"") == 1
84
+ ```
85
+
86
+ `FakeWebConnector` replays the real call sequence — `clientVersion`, `authenticate`, the
87
+ `sendRequestXML`/`receiveResponseXML` loop, `closeConnection` — and raises if the session
88
+ doesn't terminate, which catches runaway iterators in milliseconds instead of in the
89
+ connector's log. `FakeQuickBooks` answers qbXML the way QuickBooks does: paged iterators,
90
+ `MaxReturned`, status 1 for an empty result, status 3100 for an unsupported request.
91
+
92
+ ## The parts that bite
93
+
94
+ **Status codes ride on successful envelopes.** A request QuickBooks refused (status 3100,
95
+ "not available in this edition") comes back as a perfectly well-formed response document with
96
+ no records in it. If you parse for rows and ignore the status, an unsupported request is
97
+ indistinguishable from an empty table, and a cache built on top of it degrades into empty
98
+ results without anything logging an error. Every `Response` here carries its status, `ok`
99
+ distinguishes "nothing found" (status 1, genuinely fine) from "never ran", and
100
+ `raise_for_status()` is one call away.
101
+
102
+ **Element order is part of the schema.** qbXML is a sequence, not a bag. `MaxReturned` before
103
+ the filters, `EditSequence` before the fields being changed. The builders emit the right order
104
+ so you pass a dict and stop thinking about it.
105
+
106
+ **Writes use optimistic concurrency.** Every `Mod` request must carry the `EditSequence` from
107
+ the last read, and a stale one is rejected rather than silently clobbering another user's
108
+ edit. `qbxml.mod()` requires it as a keyword argument for that reason.
109
+
110
+ **Iterators only exist on some entities.** Asking for one on an entity that doesn't support it
111
+ is an opaque parse error from QuickBooks, so `qbwc-kit` raises at build time instead.
112
+
113
+ **Returning 100 ends the session.** `receiveResponseXML` returns percent complete, and a
114
+ progress calculation that rounds up too early silently truncates the sync. `Session.progress()`
115
+ caps at 99 until the work is genuinely done.
116
+
117
+ **An unknown ticket is normal.** Restart the server mid-update and the next callback arrives
118
+ with a ticket that no longer exists. Faulting makes the Web Connector retry forever; this
119
+ service tells it the session is over instead.
120
+
121
+ ## Layout
122
+
123
+ | Module | What it does |
124
+ | --- | --- |
125
+ | `qbwc_kit.soap` | The small SOAP 1.1 slice QBWC actually uses — parse a call, build a response or fault |
126
+ | `qbwc_kit.qbxml` | Request builders (`query`, `add`, `mod`) and a status-aware response parser |
127
+ | `qbwc_kit.session` | Generator-based tasks, the request/response loop, ticket store with TTL |
128
+ | `qbwc_kit.service` | The eight callbacks, framework-agnostic |
129
+ | `qbwc_kit.wsdl` | WSDL generation, plus the `.qwc` file users import into the connector |
130
+ | `qbwc_kit.server` | Optional FastAPI adapter |
131
+ | `qbwc_kit.testing` | `FakeWebConnector`, `FakeQuickBooks` |
132
+
133
+ ## Example
134
+
135
+ [`examples/sync_to_sqlite.py`](examples/sync_to_sqlite.py) is a complete integration: it
136
+ mirrors customers and invoices into SQLite, syncs incrementally off a stored watermark, and
137
+ generates the `.qwc` file to import into the Web Connector.
138
+
139
+ ```bash
140
+ python examples/sync_to_sqlite.py --write-qwc mirror.qwc # generate the connector file
141
+ python examples/sync_to_sqlite.py --url https://books.example.com/qbwc
142
+ ```
143
+
144
+ Two details in there are worth stealing. The watermark only advances after every page has been
145
+ written, so an interrupted run repeats work instead of skipping it. And it is rewound by a
146
+ minute, because QuickBooks stamps `TimeModified` from the workstation clock — a record saved
147
+ during the sync can otherwise land just behind the watermark and never be picked up again.
148
+
149
+ The example is covered by the test suite, so it can't drift from the library.
150
+
151
+ ## Deployment notes
152
+
153
+ - QBWC will not accept an endpoint on plain HTTP unless it is `localhost`. Use TLS.
154
+ - `soap:address` in the WSDL has to be the URL the connector actually posts to. Behind a
155
+ reverse proxy, that is the public URL, not `http://localhost:8000`.
156
+ - `OwnerID` and `FileID` in the `.qwc` file identify your integration to QuickBooks.
157
+ Generate them once and keep them; changing them forces every user to re-authorise.
158
+ - The connector authenticates with a password stored in Windows' credential store. Treat the
159
+ `authenticate` callback as a real auth boundary — `StaticAuthenticator` compares with
160
+ `secrets.compare_digest`, and anything you write should too.
161
+
162
+ ## Scope
163
+
164
+ Read and write access to list and transaction entities through qbXML, which is what the Web
165
+ Connector exposes. Not covered: QuickBooks Online (that has a real REST API — use it),
166
+ qbposXML for Point of Sale, and the direct COM `QBFC` interface, which needs code running on
167
+ the same Windows machine as QuickBooks.
168
+
169
+ ## Development
170
+
171
+ ```bash
172
+ pip install -e '.[dev]'
173
+ pytest -q
174
+ ruff check . && ruff format --check .
175
+ ```
176
+
177
+ ## License
178
+
179
+ MIT
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "qbwc-kit"
7
+ version = "0.1.0"
8
+ description = "Talk to QuickBooks Desktop over the Web Connector: SOAP callbacks, qbXML, and test doubles."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Eren Altuntas" }]
13
+ keywords = ["quickbooks", "qbxml", "web-connector", "qbwc", "soap", "accounting", "erp"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Office/Business :: Financial :: Accounting",
23
+ "Topic :: Software Development :: Libraries",
24
+ ]
25
+ dependencies = []
26
+
27
+ [project.optional-dependencies]
28
+ server = ["fastapi>=0.100"]
29
+ dev = ["pytest>=7", "fastapi>=0.100", "httpx>=0.24", "ruff>=0.4"]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/meister5/qbwc-kit"
33
+ Issues = "https://github.com/meister5/qbwc-kit/issues"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.pytest.ini_options]
39
+ testpaths = ["tests"]
40
+ filterwarnings = ["ignore::DeprecationWarning"]
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py310"
45
+ src = ["src", "tests", "examples"]
46
+ # Code blocks in the README are hand-aligned for reading; recent ruff versions
47
+ # format markdown too, and that alignment is not worth a red CI badge.
48
+ extend-exclude = ["*.md"]
49
+
50
+ [tool.ruff.lint]
51
+ select = ["E", "F", "I", "UP", "B", "SIM"]
52
+ ignore = ["E501", "B008"]
53
+
54
+ [tool.ruff.lint.per-file-ignores]
55
+ "tests/*" = ["E402"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,69 @@
1
+ """qbwc-kit: talk to QuickBooks Desktop over the Web Connector.
2
+
3
+ QuickBooks Desktop has no HTTP API. The only supported way in is the Web
4
+ Connector: a Windows service that polls *your* SOAP endpoint on a schedule,
5
+ asks it for qbXML, hands that to QuickBooks over COM, and posts the response
6
+ back. Every integration therefore has to implement the same eight callbacks and
7
+ the same request/response loop before it can read a single invoice.
8
+
9
+ This package is that plumbing:
10
+
11
+ * :mod:`qbwc_kit.soap` - the small SOAP slice QBWC actually uses
12
+ * :mod:`qbwc_kit.qbxml` - qbXML request building and status-aware parsing
13
+ * :mod:`qbwc_kit.session` - generator-based tasks spanning many round trips
14
+ * :mod:`qbwc_kit.service` - the eight callbacks, framework-agnostic
15
+ * :mod:`qbwc_kit.server` - optional FastAPI adapter and WSDL hosting
16
+ * :mod:`qbwc_kit.testing` - a fake Web Connector and a fake QuickBooks
17
+
18
+ The core has no dependencies outside the standard library.
19
+ """
20
+
21
+ from . import qbxml
22
+ from .qbxml import (
23
+ QBXMLRequest,
24
+ QBXMLStatusError,
25
+ Request,
26
+ Response,
27
+ ResponseSet,
28
+ add,
29
+ mod,
30
+ parse_response,
31
+ query,
32
+ )
33
+ from .service import QBWCService
34
+ from .session import (
35
+ Authenticator,
36
+ Session,
37
+ SessionStore,
38
+ SimpleTask,
39
+ StaticAuthenticator,
40
+ Task,
41
+ TaskContext,
42
+ )
43
+ from .wsdl import build_qwc, build_wsdl
44
+
45
+ __version__ = "0.1.0"
46
+
47
+ __all__ = [
48
+ "Authenticator",
49
+ "QBWCService",
50
+ "QBXMLRequest",
51
+ "QBXMLStatusError",
52
+ "Request",
53
+ "Response",
54
+ "ResponseSet",
55
+ "Session",
56
+ "SessionStore",
57
+ "SimpleTask",
58
+ "StaticAuthenticator",
59
+ "Task",
60
+ "TaskContext",
61
+ "__version__",
62
+ "add",
63
+ "build_qwc",
64
+ "build_wsdl",
65
+ "mod",
66
+ "parse_response",
67
+ "qbxml",
68
+ "query",
69
+ ]
@@ -0,0 +1,40 @@
1
+ """qbXML request building and response parsing."""
2
+
3
+ from .builder import QBXMLRequest, Request, add, element, elements, mod, query, ref
4
+ from .parser import (
5
+ QBXMLParseError,
6
+ QBXMLStatusError,
7
+ Response,
8
+ ResponseSet,
9
+ parse_response,
10
+ )
11
+ from .types import (
12
+ ITERATOR_ENTITIES,
13
+ STATUS_NOTHING_FOUND,
14
+ STATUS_OK,
15
+ STATUS_UNSUPPORTED_REQUEST,
16
+ OnError,
17
+ Severity,
18
+ )
19
+
20
+ __all__ = [
21
+ "ITERATOR_ENTITIES",
22
+ "OnError",
23
+ "QBXMLParseError",
24
+ "QBXMLRequest",
25
+ "QBXMLStatusError",
26
+ "Request",
27
+ "Response",
28
+ "ResponseSet",
29
+ "STATUS_NOTHING_FOUND",
30
+ "STATUS_OK",
31
+ "STATUS_UNSUPPORTED_REQUEST",
32
+ "Severity",
33
+ "add",
34
+ "element",
35
+ "elements",
36
+ "mod",
37
+ "parse_response",
38
+ "query",
39
+ "ref",
40
+ ]