A desktop shell embedding a browser with two tabs (Odoo + MISA), a left sidebar for sync actions, logs panel, and an internal skeleton for MISA ↔ Odoo sync.
- Two tabs open on startup:
- Odoo:
ODOO_BASE(defaulthttp://localhost:8069) - MISA:
https://amisapp.misa.vn/login/
- Odoo:
- Sidebar options (mock):
- "Sync Kho hàng" (Inventory) → runs a background diff job
- "Sync Sản phẩm", "Sync Đối tác" placeholders
- Bottom log area shows job lifecycle and results
python -m venv .venv
. .venv\Scripts\Activate.ps1
pip install --upgrade pip
pip install -r requirements.txt
python run_app.py./build_windows.ps1 -Clean
# Output: dist/SyncPythonApp.exeapp/
main.py # Qt entry
ui/main_window.py # Sidebar + tabs + logs
widgets/browser_widget.py
jobs/runner.py # QThread-based background jobs
sync/ # Adapters & diff
misa_adapter.py # Export/parse skeleton (mock)
odoo_adapter.py # Import/export skeleton (mock)
sync_service.py # Diff by key utilities
resources/ # (icons/assets placeholder)
- Trigger: click "Sync Kho hàng" in sidebar.
- Background job (non-blocking):
MisaAdapter.iter_inventory_records()yields source records (mocked now).OdooAdapter.get_existing_inventory()yields existing target records (mocked).diff_records(source, target, key="product_code")returns sets:to_create,to_update,to_remove.- Display a small summary in the log area.
- Next step (to implement): call
OdooAdapter.import_inventory(...)for creates/updates.
- Uses
JobRunnerwithQThreadto avoid blocking the UI while parsing/converting large Excel files or performing comparisons. - Future work: parsing Excel (ClosedXML/openpyxl/pandas) and converting to Odoo CSV can run inside a
JobRunnerjob and store intermediate files in an app-owned temp dir.
- Implement real export from MISA to
.xlsxand parse to normalized records. - Implement mapping to Odoo CSV columns; call Odoo import or RPC.
- Add a Config page (inside the app) to manage credentials and mappings.
- Persist app settings under an internal config directory (e.g.,
%LOCALAPPDATA%/SyncPythonApp).
- Sidebar action "Khoi tao ket noi an toan" runs key provisioning and the bootstrap handshake in a background job. Keys persist under app/resources/security/node_public.pem and app/resources/security/desktop_private.pem.
SecureSyncClientperforms provisioning, encrypts{misa, hhwms}with RSA-OAEP(SHA-256), and signs canonical strings with Ed25519 before sending over HTTPS.- Every
/api/**request is signed (X-Timestamp,X-Signature,X-Sign-Alg). The middleware validates signatures, decrypts the bootstrap payload once, and reuses cached tokens for outgoing MISA/Odoo calls. TLS remains mandatory (ALLOW_HTTP_DEV=trueonly for local development). - Middleware stores generated keys (RSA private/public, desktop public) under security_artifacts/ so restarts keep trusted materials.
| Variable | Purpose |
|---|---|
SYNC_MIDDLEWARE_BASE_URL |
HTTPS base URL of the Node middleware (e.g. https://localhost:3443). |
ODOO_BASE |
Base URL of the Odoo/HHWH instance used across desktop flows (e.g. http://localhost:8069). |
SYNC_BOOTSTRAP_FILE |
Path (relative to app/ or absolute) to the JSON file containing misa and hhwms bootstrap payloads. |
The bootstrap file stores cookies as structured JSON (matching browser exports); the desktop app converts them to header strings when contacting the middleware.
- New endpoints under
/api: key provisioning (/keys/generate-node,/keys/generate-desktop) and secure bootstrap (/bootstrap). - All
/apidata routes (/customers,/quotations, etc.) now require signed GET requests; tokens are pulled from the bootstrap cache instead of.envwhen present. - TLS is enforced server-side; run behind a TLS terminator or configure HTTPS bindings. Use
ALLOW_HTTP_DEV=trueonly during local development.
Each sync runs in its own feature module under app/features/ so code and state do not interfere:
app/
core/
app_paths.py # app-local data dirs
selenium_driver.py # persistent Chrome profile factory
session_store.py # cookies/tokens JSON store
features/
inventory_sync/
service.py # orchestrator for inventory
misa/
session.py # access to MISA Chrome profile + cookies
export.py # (placeholder) selenium flow to export inventory
odoo/
importer.py # (placeholder) import flow
product_sync/
__init__.py
partner_sync/
__init__.py
- Selenium profiles are stored inside the app data dir:
%LOCALAPPDATA%/SyncPythonApp/profiles/<site>so sessions/cookies persist across runs without touching outside folders. - Feature-specific working files go to
%LOCALAPPDATA%/SyncPythonApp/features/<feature>and temp files to%LOCALAPPDATA%/SyncPythonApp/tmp/<feature>.
- In the UI, click "📦 Sync Kho hàng". It runs
features.inventory_sync.service.run_diff_onlyin a background thread and prints a summary in logs. - To connect to real MISA: implement the Selenium steps in
features/inventory_sync/misa/export.py(after login, navigate and export), then parse and map to Odoo before import.
- Click "🧾 Sync Sản phẩm" in the sidebar.
- Background steps:
- Export Excel từ MISA (Selenium + session lưu trong
%LOCALAPPDATA%/SyncPythonApp/profiles/misa-product). Hiện tại mock tạo file để minh họa. - Convert sang CSV định dạng Odoo (
name, default_code, barcode, list_price, standard_price, uom_id/name). - Import vào Odoo (stub đếm số dòng) — thay bằng RPC/CSV import thật khi có thông tin kết nối.
- Export Excel từ MISA (Selenium + session lưu trong
- Files luôn nằm trong thư mục nội bộ:
- MISA exports:
%LOCALAPPDATA%/SyncPythonApp/features/product/misa_exports/ - Odoo imports:
%LOCALAPPDATA%/SyncPythonApp/features/product/odoo_imports/
- MISA exports:
- Log hiển thị tiến trình ở panel phía dưới UI.