Standard aluminum frames per shop drawings estimate
✅
Louvers per elevation drawings estimate
✅
Trickle vents on operable units contract
✅
Standard delivery to curb estimate
❌
Crane / hoist contract
❌
Tariff increases above baseline contract
❌
Site protection / scaffolding estimate
❌
Permits & filings estimate
Live charges recent
Reconcile
Avrumi's queue — match QBO charges to projects + promises
Reconcile workflow preview coming next.
How PM Profit works
What it does, where the data comes from, and the workflows it supports
Mission
Make sure every Vistaza job stays profitable. See in real time the basis of the price when it was sold, every cost as it lands, and a single number — margin remaining — that tells you whether you're still on track or burning into profit. Help decide what's billable, catch over-runs early, and never lose track of a charge that should've been invoiced back to the customer.
What the app does
Locks the baseline at contract sign — captures the Hub-sheet cost basis, the signed estimate, the contract, the inclusion/exclusion scope, and the final sell price as a frozen snapshot.
Tracks live cost per job across factory, shipping, louvers, trickle vents, customs, sales tax, and ongoing QCJ (travel, jobsite prep, window school, techs, salesppl visits, pretest, testing, service).
Shows margin remaining as the headline number — green / yellow / red — so you know at a glance whether a job is still healthy.
Streams charges in real time from QuickBooks, Drive (factory PIs), email forwards, manual entry, and WhatsApp screenshots.
Flags billable extras by comparing every new charge against the contract scope — if it was excluded, the chip says "billable extra" so you can bill it back.
Tracks the latest known cost per category — the current factory PI, the current container quote, the current customs/tariff number — and keeps the linked proof document attached.
Requires proof on every cost (soft rule) — every line shows a 📎 source or a ⚠️ "no proof" chip, with a sweepable filter.
Reconciles QBO charges to projects — charges with a project tag flow in automatically; untagged ones land in Avrumi's queue.
Catches the unexpected — over-baseline categories, untagged QCJ, promised items not yet paid, factory PI versions that bumped pricing.
Portfolio view — see every job in a glance, sort by risk or margin, identify which jobs need attention before they cost money.
The lifecycle of a job in PM Profit
Step 1
Draft
Deal won in Pipedrive. Hub sheet exists. Contract being prepared. App pulls deal info but no baseline yet.
Charges flow in from QBO, Drive, email, manual. Each cost line updates with the latest known number + proof. Margin number updates in real time.
Step 4
Triage + decide
Over-baseline alerts surface. Charges flagged as billable extras get billed back. Avrumi reconciles untagged QBO charges. Decisions stay in front of you.
Step 5
Close out
Final reconcile when delivered. Final margin recorded. Lessons inform the next baseline. Job archived but searchable.
Where the data comes from
🟢 Hub Sheet
Google Sheet linked from Pipedrive. First tab snapshot at lock = baseline cost + internal sell. Per-deal.
Baseline — frozen snapshot of cost lines + sell price at contract sign. Pulled from Hub first tab. Never changes after lock.
Current cost — live sum of latest-known per category. Updates as new PIs, bills, CC charges, manual entries land.
Margin remaining — Sold − Current. The headline number. Green >25%, orange 15–25%, red <15%.
Proof rule — every cost number should have a linked source (📎). If not, ⚠️ shows on the line. Soft rule: you can save without proof, but it's visible.
Scope checklist — structured ✅/❌ inclusions list on top of the estimate + contract PDFs. Lets the app flag charges that match excluded items as "billable extra."
QCJ lifecycle — Promised → Performed → Charged → Reconciled. Lightweight; promises are optional, not central. Avrumi matches QBO charges to projects.
Risk states — Healthy / Watch / At risk / Draft. Color-coded across cards, tables, and bars so risk is obvious without reading numbers.
Multi-project dashboard. Card per job with margin %, sold/baseline/current/Δ, burn bar, alert chips. Filter by risk/status/owner. Click a card to drill in.
Cross-project live feed of every cost as it lands. Source pills (QBO / Drive / manual / email / WhatsApp). Unmatched queue with auto-suggest. "Billable extra" routing.
Feeds: Deal #, Project ID (=deal_id+7000), GC, address, owner, stage, Won date, sold value, Hub sheet URL (custom field hash 98ee...), Drive folder URL (83cc...), Asana search URL (4a98...), QuickBooks customer URL (4e55...).
ready · API token
use same token as Install app
🟡
Google Drive — Active Projects
Root folder per job. Standard subfolders: 01-Vistaza shops, 02-Plans, 03-Customer/Estimates, 04-Vendors, 05-Jobsite, 06-Pictures, 97-Other.
Feeds: Latest factory PI (Vendors), signed estimate PDF (Customer/Estimates), signed contract PDF, broker bills, photos, shop drawings. Each cost line tags one file as "current source."
ready · service account
reuse Install app SA; share folders
🟣
QuickBooks Online
Vistaza QBO tenant. Linked per deal via Pipedrive custom field (4e55...) → QBO customer URL.
Feeds: Vendor bills + CC charges tagged to project. Most charges already tagged. Untagged ones → Avrumi's Reconcile queue. Vendor memos for category auto-assignment.
needs OAuth setup
per Install app handoff_finance_track.md
🟠
Asana — PM Hub
Project gid 1201320434118343. Task template has ~12 tasks per job across PM Hub + Finance Tasks + Installation Management + PM Finance sections. Custom field Project ID = deal_id + 7000.
Feeds: PM owner, project stage, milestone dates. Optional custom-field cost overrides per cost line. Search URL already in Pipedrive custom field (4a98...).
ready · API token
same token as Install app
New — needs to be created
🔥
Firebase project — vistaza-pm-profit
New Firebase project under aj@vistaza.com. Enable: Hosting, Auth (Google, @vistaza.com), Firestore, Storage, Functions (Python, gen 2, us-east1).
Cloud Function reads the Hub sheet URL from Pipedrive, opens the first tab, pulls named cells into the baseline snapshot (factory, shipping, louvers, trickle vents, customs, sales tax, sell price).
Blocker: Hub sheet needs a consistent cell layout / named ranges across all projects. Pick named cells vs positional extraction.
needs setup
also needs sheet template decision
✉️
Email forward alias — charges@vistaza.com
Google Workspace alias or group. Anyone forwards a charge (email receipt, WhatsApp screenshot) → parsed by Cloud Function → pending queue in app.
Feeds: ad-hoc charges from WhatsApp, vendor receipts, sub invoices that don't go through QBO yet.
needs setup
create alias + Gmail push
👤
Avrumi (bookkeeper) — user role
Needs a user role with access to Reconcile page, charge-to-project assignment, promise reconciliation. Non-admin (no margin $ visibility) or admin — your call.
Workflow: Daily sweep of unmatched QBO charges, assign to project, flag billable extras, match promises to paid bills.
needs setup
Firestore role matrix
v2 / later
💬
WhatsApp Business API
Meta's official API cannot read group messages. v1 path: manual "+ Log charge" with paste-screenshot. v2 path: forward to charges@vistaza.com from the group.
Feeds: ad-hoc charges discussed in WhatsApp groups between AJ, subs, vendors.
v2 / skip
official API not viable for groups
🔔
Real-time notifications
Push alerts when a charge lands that would push a project over baseline, or when an unmatched charge sits >24h, or when a billable extra is pending bill-out.
Channels: email digest daily, push notification on PWA, maybe WhatsApp to AJ.
v2
after live tracking works
End-to-end data flow
Pipedrive→ deal info + hub-sheet URL + drive-folder URL pulled bi-monthly into Firestore projects/{deal_id}
Hub Sheet→ on "Lock baseline" click, Cloud Function reads first tab → writes Firestore projects/{id}/baseline snapshot (frozen)
Drive→ latest factory PI, contract PDF, broker bills linked per cost line. File version checked on refresh. Thumbnails cached.
QBO→ Cloud Function polls for new bills + CC. Project-tagged → auto-route. Untagged → Reconcile queue.
Manual+ Email → Cloud Function receives email from charges@vistaza.com or form submission → lands in pending queue.
Asana→ project status + milestones sync on read. PM owner pulled once per hour.
All of the above writes flow through the same Firestore documents. The dashboard reads from Firestore + renders. No client-side writes of unconfirmed state — everything goes through a Cloud Function so nothing ever becomes a "ghost record."