Avrumi's queue — match QBO charges to projects + promises
Reconcile workflow preview coming next.
Help
What PM Profit does, where the data comes from, and the connections behind it
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."
Connection status · snapshot Apr 17, 2026
Admin
Permissions + activity log — visible only to AJ
Permissions
Two operator types: Pricing operators → Pricing side only. Finance operators (e.g. Jay, Avrumi) → the full project side incl. Billing, same as Owner. Owner is hardcoded for lockout protection. Stored in pmp_access/_acl (operators / finance arrays) — writable only by AJ.
Loading access list…
Orders & Quotes access
Who can open the ordering tools — /checkquote (price a factory quote vs what we've historically signed, with drawings) and /quoteprep (redact a quote to send out). Separate, tighter allowlist from the Pricing/Finance roles above: only the people added here (plus Owner) get in — no one else, even other @vistaza.com users. Stored in pmp_access/_acl.checkquote.
Loading access list…
Activity log
Append-only audit trail — sign-ins, baseline locks, cost lines, permission changes. Owner-only · newest first · last 100 events.
Loading activity…
Newer build available.
Billing
🔒 Finance + Owner · pushes to QuickBooks
Committed (signed)
—
Invoiced
—
left to invoice —
Owed (invoices sent)
—
Signed jobs
loading…
Job
Customer
PM stage
Signed total
Billed
Next invoice
Loading signed jobs…
Committed (signed)
—
loading…
Invoiced (QBO)
—
live from QuickBooks
Left to invoice
—
Collected (paid)
—
Cash flow projection
Not wired yet — the outgoing side (factory POs / purchasing schedule) needs to flow in first. Once it does, this shows incoming (from the milestone schedule + QBO) vs outgoing per period — "what we need to execute the jobs."
Sold (signed)
—
loading…
Baseline cost
—
locked Hub baselines · total cost
Margin (draft)
—
sold − baseline · pending actuals
Live overage
—
QBO live · bookkeeping not final
Assumed vs actual
Job
Customer
PM stage
Sold
Baseline
Current
Δ
Margin
Loading jobs…
Invoicing & payments
Date
Type
Ref #
Customer
Amount
Balance
Status
Open this tab to load…
Source spec — AJ voice notes, 2026-06-03
The brief that defined this module. Transcribed and cross-verified (whisper medium vs large-v3; ambiguous words re-sliced; AJ confirmed "Glide" and "send shops"). Highlighted = AJ-confirmed.
Clip 118.54.15 · ~79s
Hi AJ, hi Claude. Okay, here's what Glide did that we want to replace, and maybe even a bit more.
Number one: we want — every time we save a job that we finalize on — we want to be able to submit that along with payment terms, and for that to be stored somewhere where we can track. We can log in and see, like, kind of a dashboard where that project is up to in terms of payment one, two, three, four, and what it is connected to.
The connections would be triggers. So as we go along, there would be a trigger that triggers that invoice to be sent — and it would be prepared in QuickBooks and sent to Jacob's email to send out to the customer.
And the triggers would be: when we put something in project management, into that queue and Pipedrive. Then as we go along, when we send shops, it should create an invoice to put the windows in production. When we put it in production, it should be set on a trigger — maybe a delay — to create an invoice to ship the windows. And then once it ships, to create a trigger to create an invoice for delivery.
Clip 218.54.56 · ~15s
Then it would be good to be able to track everything on a project level, and also on a global level — to see: how much do we have committed, how much have we invoiced, and then how much is still left to invoice.
Clip 318.55.31 · ~31s
If we want to go even further, then we also want to save all the assumed numbers from the sheet, and then track incoming and outgoing money in QuickBooks assigned to the project, and see how we are against invoicing and also how we are against purchasing — to be able to compare budget, to track where we're up to now. And that'll be able to feed into a cash flow, to see what we need in order to execute the jobs.
Spot
medium
large-v3
Resolved
"Glide"
Lide
Lide/Lied
Glide — AJ
"payment 1·2·3·4"
garble (famut)
payment (stable ×3)
payment
"send shops"
shots
shots
shops — AJ
"Jacob"
Jacob
Jacob
Jacob
Both engines agreed on the full structure; only 3 tokens needed re-slicing. Originals: ~/Downloads/WhatsApp Audio 2026-06-03 at 18.5*.opus