A Flask app for a CNC/laser cutting company (Trafcom): upload a DXF drawing and get its cutting price calculated automatically from geometry (area, cut length, pierce count), build a catalog of reusable "Details" and "Products" from priced drawings, track stock via delivery notes, and run customer orders through production tracking. The UI is in Bulgarian.
- Features
- Requirements
- Installation
- Running it
- Project structure
- Schema changes
- Tests
- Configuration notes
- DXF pricing — upload a DXF file, get area/cut-length/pierce-count
extracted from the geometry and run through a time-based pricing engine:
material supplies the raw-stock area cost plus its cutting/pierce speed,
and one or more billable
Services (see below) supply the EUR/hour rate — a job can be priced against several services at once (e.g. a combined cut+engrave pass), plus a flat setup fee. - Services & Operations — admins define billable
Servicetypes (e.g. laser cutting, 3-axis milling) with an EUR/hour rate and the machines that can run them; extra post-processing steps on a catalogDetail(Operation— milling, deburring, welding, ...) are costed by duration × the linked service's rate. - Catalog — admin-curated
Detail(single part) andProduct(assembled from multiple Details + extra costs like paint/assembly, with markup) records, each with stock tracking and a printable barcode label. - Orders & production tracking — customer orders (
Order/OrderItem) roll up completion status per-component as production reports units produced, driven off frozen recipe snapshots so later catalog edits don't retroactively change placed orders. - Production wizard — separately from order tracking above, plan a standalone "produce N of this Detail" job ahead of demand; it's clamped so it can never drive the chosen material batch's stock negative, and completing/deleting one properly moves (or reverses) real stock.
- Delivery notes / stock intake — recording goods received from a
Supplierbumps stock on the referenced material/detail/product. Material requests raise the restock ask on the other end, from the storage dashboard, when stock runs low. - Clients & deliverers — lightweight lookup catalogs (with Bulgarian company legal fields — ЕИК, ДДС №, address, МОЛ) an order can reference.
- Label printing — Code128 barcode labels (rendered as inline SVG, no external service) for any catalog entry or produced batch.
- Role-based access —
regular_user,worker,admin,web_designer, with a scoped-down content editor for non-pricing public-page text; new registrations can be locked from/admin/userswithout affecting existing accounts. - Two-factor auth & email verification — optional per-user TOTP 2FA (QR-code setup, no image file written to disk) and email verification/ password-reset links, both signed & time-limited rather than stored as a token column.
- Quotes (Offers) — pre-order quotes with product/detail/text-only
lines, an optional whole-quote discount and expiry date, per-line photos,
and one click to turn the checked lines into a real
Order. - Offer / protocol / certificate documents — browser-print pages (Ctrl+P to PDF), no server-side PDF library.
- Panel Generator — a parametric panel/hole-pattern DXF tool (square,
hexagon, triangle, rhombus, hex-cluster), with save/reload presets and a
server-side (
ezdxf) export that real CAD software can open cleanly. - AI chat assistant (optional) — a chat widget backed by Claude, with read-only tools to look up catalog items, stock, orders, and prices in plain Bulgarian; degrades to a friendly "unavailable" message if no API key is configured.
- Activity log — every state-changing admin/worker action is recorded
(who, what, when), exportable to
.xlsx. - Missing-stock dashboard — every open order that doesn't currently have enough Detail/Product stock to fulfill it, recomputed live rather than stored.
- DXF revision history per Detail — the original uploaded
.dxffiles behind a catalog Detail are kept and downloadable (admins only), not just the geometry extracted from them. - Order-time customization — a standalone-detail order line can carry
its own ad-hoc operations (independent of any
Operationpermanently attached to the Detail catalog row) and an optional reference PDF (drawing/spec), both picked at order-creation time and frozen into that line's price and paperwork. - Live power monitoring — a real-time dashboard (
/admin/power) polling Shelly energy meters on the shop LAN (both Gen1 and Gen2 devices supported), plus a background poller that logs readings locally every minute so historical charts are available for both generations — not just the roughly 45-day on-device retention Gen2 meters offer natively; seedocs/SHELLY_API.md.
- Python 3
- PostgreSQL (reachable via
DATABASE_URL)
pip install -r requirements.txtCopy .env.example to .env and fill in real values:
SECRET_KEY=
DATABASE_URL=postgresql+psycopg2://user:password@localhost:5432/cnc_calculator_db
FLASK_DEBUG=0
SECRET_KEY and DATABASE_URL are required — the app raises an error on
import if either is missing. .env is loaded automatically via
python-dotenv and is gitignored.
Everything else in .env.example is optional, each feature just degrades
gracefully without it: ANTHROPIC_API_KEY (+ optional
ANTHROPIC_WORKSPACE_ID) powers the AI chat assistant; SMTP_HOST /
PORT / USER / PASSWORD / FROM control outgoing password-reset and
email-verification mail (defaults to a local localhost:25 transport with
no auth if unset).
python app.pyThis also creates any missing database tables (db.create_all() — it does
not migrate existing ones, see Schema changes below)
and seeds default material prices and machine cards on first run, then
opens http://127.0.0.1:5000/ in your browser. Set FLASK_DEBUG=1 for the
interactive debugger and auto-reload during local dev — never in a public
deployment.
There is no auto-created admin account. To create or reset one:
python -m migration.change_admin_passwordFor production, serve wsgi.py with gunicorn/waitress instead of running
app.py directly (the WSGI entrypoint never runs the dev-server
initialization block above, so run python app.py once by hand first to
set up the database).
Click to expand
app.py— models, DXF geometry/pricing logic, and all routes (single file)wsgi.py— production entrypoint (gunicorn/waitress)gunicorn.conf.py— bumps the worker timeout to 120s (/admin/power/historycan take ~25s per day-chunk fetched, close to gunicorn's 30s default)requirements.txt.env.exampleuploads/— private scratch folder for in-flight DXF uploads (not on GitHub — gitignored)
templates/ — Jinja2 templates, one per page/route
about.htmlaccount.htmlaccount_2fa_setup.html— TOTP 2FA setup, QR code rendered as inline SVGadmin.htmladmin_client_delivery_note_print.htmladmin_client_delivery_notes.htmladmin_clients.htmladmin_delivery_note_print.htmladmin_delivery_notes.htmladmin_details.htmladmin_generator_presets.html— every user's saved Panel Generator presetsadmin_log.html— activity-log audit trailadmin_material_history.htmladmin_materials.htmladmin_missing_stock.htmladmin_offer_edit.htmladmin_offer_print.htmladmin_offers.htmladmin_power.htmladmin_production_orders.html— the production wizard (build stock ahead of demand)admin_products.htmladmin_services.html— manage billableServicetypes (EUR/hour rate, linked machines)admin_users.htmlcertificate.htmlcontact.htmlcontent_editor.htmldetail_dxf_dashboard.html— per-Detail DXF revision historyedit_window.htmlerror.html— shared template for every custom error page (404/403/429/500)forgot_password.htmlgenerator.html— parametric panel/hole-pattern DXF generatorindex.htmllabel.htmllibrary.html— personal DXF upload library (/dashboard)login.htmllogin_2fa.html— TOTP code prompt for accounts with 2FA enabledmachines.htmlmy_orders.htmloffer.htmlorder_create.htmlproduct_edit.htmlproduction_report.htmlprotocol.htmlregister.htmlreset_password.htmlservices.htmlstorage_dashboard.htmlstorage_details.htmlstorage_materials.htmlstorage_products.htmlstorage_requests.html— MaterialRequest dashboard (restock asks, separate from DeliveryNote intake)upload.html
partials/ — shared chrome (navbar, footer, CSRF field, editable-text blocks)
- [`navbar.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/navbar.html)
- [`footer.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/footer.html) — also renders the AI chat widget for logged-in users
- [`editable.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/editable.html)
- [`csrf_field.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/csrf_field.html)
- [`material_options.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/material_options.html)
- [`legal_fields.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/legal_fields.html) — shared ЕИК/ДДС №/address/МОЛ fields (Client/Deliverer/Supplier)
- [`favicon.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/favicon.html)
- [`breadcrumbs.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/breadcrumbs.html)
- [`local_business_schema.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/local_business_schema.html) — LocalBusiness JSON-LD for the public pages
- [`sticky_cta.html`](https://github.com/protoknight12/Trafcom_Website/blob/main/templates/partials/sticky_cta.html) — mobile call/contact bar for anonymous visitors
static/ — CSS, client-side JS, images, and web-accessible uploads
js/dxf_viewer.js— canvas rendering of DXF geometryjs/inline_edit.js— front-end for inline content editingcss/style.cssimg/uploads/products/— persistent, web-accessible product images
migration/ — one-off migration/backfill/seed scripts (see below)
change_admin_password.pybackfill_erp_numbers.pybackfill_service_machine_cards.pyseed_real_machines.pyseed_test_data.py- ...plus the various
migrate_*.pyschema-change andbackfill_*.pydata-backfill scripts (Shelly device/reading tables, the services pricing refactor, ...) — browse the full folder
testing/ — test scripts (see below)
test_label_barcode.pytest_cnc_pricing_engine.pytest_sheet_dimensions.pytest_rate_limiting.pytest_delivery_note_stock.pytest_delivery_note_matching.pytest_web_designer_role.pytest_eik_validation.pytest_service_sections_grouping.pytest_material_option_format.pytest_security_fixes.pytest_quick_create_material.pytest_quick_create_product_components.pytest_detail_pdf_upload.pytest_operation_description.pytest_order_item_operations.pytest_order_item_attachment.pytest_shelly_status.pytest_shelly_history_aggregation.pytest_shelly_device_routes.pytest_shelly_reading_log.pytest_admin_power_history_gen1.pytest_upload_and_machine_helpers.pytest_material_only_detail_pricing.pytest_material_cost_by_type.pytest_length_based_operation_cost.pytest_operations_breakdown_excludes_cutting.pytest_operations_form_novalidate.pytest_production_orders.pytest_production_order_clamp.pytest_production_order_delete.pytest_production_stock_bump.pytest_material_requests.pytest_offer_generator.pytest_offer_create_order.pytest_detail_dxf_cyrillic.pytest_polyline_entity.pytest_generator_dxf_export.pytest_generator_presets.pytest_storage_materials_thresholds.pytest_delivery_note_price_split.pytest_service_show_price.pytest_activity_log.pytest_registration_lock.pytest_auth_email_2fa.pytest_email_verification.pytest_email_validation.pytest_error_pages_and_session.py
docs/ — reference docs
SHELLY_API.md— function-by-function reference for the Shelly power-monitoring integrationshelly_dobavyane_masina_BG.md— Bulgarian how-to for adding a machine on/admin/power
db.create_all() only creates tables that don't exist yet — it never
alters existing ones (there's no Alembic in this project). Any model field
change needs either a fresh dev database or a hand-written migration script
in migration/, run as a module from the repo root, e.g.:
python -m migration.migrate_add_supplier_vatSee each script's module docstring for what it does and when to run it.
Most tests are plain assert-based scripts, run as modules from the repo
root, e.g.:
python -m testing.test_label_barcodeA few use pytest instead, where the thing being tested needs real request/response behavior (redirects, status codes, session auth):
pip install pytest flask-wtf
pytest testing/test_security_fixes.py -v- Rate limiting (
flask-limiter) defaults to 300 requests/hour per IP, with tighter limits on/loginand/register. Storage is in-memory — per-process only. If running more than one gunicorn/waitress worker, switch to a shared store (e.g. Redis) or limits won't be enforced correctly across workers. - All state-changing requests require a CSRF token (
flask-wtf). - Shelly energy meters are managed from the
/admin/powerdashboard itself (add/rename/delete/relink a machine there, no restart needed). TheSHELLY_DEVICESenv var in.env.exampleis legacy, one-time-use only — it's read once on first startup after upgrading, to migrate whatever was configured there into the database; leave it blank on a fresh install. Seedocs/SHELLY_API.md.