Skip to content

implement vendor automation#99

Open
yushinj wants to merge 6 commits intomainfrom
vendor
Open

implement vendor automation#99
yushinj wants to merge 6 commits intomainfrom
vendor

Conversation

@yushinj
Copy link
Copy Markdown
Collaborator

@yushinj yushinj commented Apr 9, 2026

  • add Bill.co vendor payload/address mapping fix
  • fallback to create when existing bill_vendor_id is invalid
  • check vendor network status after sync and trigger invite/reminder when not connected
  • improve sync-all batching/aggregation efficiency
  • add/expand vendor sync tests

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
biotech-maintenance-platform Ready Ready Preview, Comment Apr 10, 2026 0:47am

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements automated Bill.com vendor synchronization for technicians, including payload mapping fixes, “update-then-create” fallback behavior, post-sync network connection checks with invite/reminder flows, and supporting API routes/tests.

Changes:

  • Added Bill.com vendor sync library + API routes (/api/bill/vendors/sync, /sync-all, /debug) and expanded technician address capture to support vendor payload requirements.
  • Implemented network-status check after vendor sync to trigger Bill.com invite and a Resend reminder email when not connected.
  • Added/updated Vitest coverage for vendor payload validation and vendor sync route authorization/error mapping.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
my-app/src/test/vendor.test.ts Adds unit tests for buildVendorPayload validation/mapping behavior.
my-app/src/test/vendor-sync-route.test.ts Adds API route tests for authZ and error/status mapping for vendor sync.
my-app/src/test/address-management.test.tsx Updates router mocks to align with navigation usage changes.
my-app/src/app/lib/email.ts Adds a new vendor connection invite/reminder email template and sender.
my-app/src/app/lib/bill/vendor.ts New vendor sync implementation: payload building, upsert logic, status persistence, network-status checks, invite/reminder trigger, and debug helpers.
my-app/src/app/lib/bill/types.ts Introduces types for vendor debugging + future AP bill automation.
my-app/src/app/lib/bill/client.ts New Bill.com client for vendor endpoints (create/update/get vendor, network status, invite).
my-app/src/app/components/EditProfile.tsx Collects technician address fields and triggers vendor sync after profile save.
my-app/src/app/components/CompleteTechInfo.tsx Collects technician address fields and best-effort triggers vendor sync after onboarding.
my-app/src/app/components/CompleteProfile.tsx Hardens session loading and token handling in profile completion.
my-app/src/app/components/AuthStatus.tsx Improves resilience around session/role lookups and sign-out cleanup.
my-app/src/app/components/AuthGate.tsx Adds error handling around session retrieval before gating.
my-app/src/app/api/create-profile/route.ts Persists technician address fields with legacy-schema fallback behavior.
my-app/src/app/api/bill/vendors/sync/route.ts Adds vendor sync endpoint with admin/self-tech authorization.
my-app/src/app/api/bill/vendors/sync-all/route.ts Adds admin-only batched “sync all pending vendors” endpoint.
my-app/src/app/api/bill/vendors/debug/route.ts Adds admin-only debug endpoint that reports sync readiness without Bill.com calls.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread my-app/src/app/api/create-profile/route.ts
Comment thread my-app/src/app/api/create-profile/route.ts Outdated
Comment on lines 226 to 250
// Prepare the request body based on role
type RequestBody = {
role: "lab" | "technician" | null
full_name: string
phone: string
lab?: {
name: string
}
tech?: {
experience: string
bio: string
company: string | null
line1: string | null
line2: string | null
city: string | null
state: string | null
zipcode: string | null
}
}

const requestBody: RequestBody = {
role: profile?.role ?? null,
role: profile?.role === "lab" || profile?.role === "technician" ? profile.role : null,
full_name: fullName,
phone: normalizedPhone
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requestBody.role is forced to null for any role other than lab/technician. Since this component can load an admin profile (type now includes admin), submitting the form would clear the user’s role in profiles and effectively remove admin privileges. Preserve admin (or omit role from the request when editing admin profiles) so admin users don’t get downgraded.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already applied in commit 4c509e6. RequestBody.role now includes "admin" and the role assignment preserves it: profile?.role === "lab" || profile?.role === "technician" || profile?.role === "admin" ? profile.role : null.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already in place from commit 4c509e6. RequestBody.role includes "admin" and the assignment on line 247 preserves it: profile?.role === "lab" || profile?.role === "technician" || profile?.role === "admin" ? profile.role : null.

Comment thread my-app/src/app/lib/bill/client.ts Outdated
Comment on lines +361 to +389
const network = await billClient.getVendorNetworkStatus(billResult.id);
log(`Vendor ${billResult.id} network status: ${network.status}${network.rawStatus ? ` (${network.rawStatus})` : ""}`);

if (network.status !== "connected") {
try {
await billClient.sendVendorConnectionInvite(billResult.id, profile.email || undefined);
log(`Sent Bill.com connection invite for vendor ${billResult.id}`);
} catch (inviteErr) {
const message = inviteErr instanceof Error ? inviteErr.message : String(inviteErr);
logError(`Bill.com invite endpoint failed for vendor ${billResult.id}:`, message);
}

if (profile.email) {
const reminder = await sendVendorConnectionInviteEmail({
recipientEmail: profile.email,
recipientName: profile.full_name,
vendorName: payloadResult.payload.name,
vendorId: billResult.id,
});

if (reminder.success) {
log(`Sent vendor connection reminder email to ${profile.email}`);
} else {
logError(`Failed to send vendor reminder email to ${profile.email}`);
}
} else {
logError(`Cannot send vendor connection reminder email: profile email missing for ${technicianId}`);
}
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invite/reminder logic triggers for any status other than connected, including unknown (when the API response doesn’t contain a parseable network status). That can generate unnecessary invites/emails on transient parsing/API issues. Only send invites/reminders when the normalized status is explicitly not_connected (and consider skipping on unknown).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already applied in commit 4c509e6. Changed network.status !== "connected" to network.status === "not_connected" so invite/reminder emails are only sent for an explicit not_connected state, skipping unknown and other transient statuses.

Comment thread my-app/src/app/api/bill/vendors/sync-all/route.ts Outdated
yushinj and others added 2 commits April 9, 2026 20:12
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…role, login URL, vendor invite status, sync-all filter

Agent-Logs-Url: https://github.com/gordon-cs/Biotech-Maintenance-Platform/sessions/fcf9c524-e7e6-41e8-aaaa-08c2927f67b7

Co-authored-by: yushinj <149107617+yushinj@users.noreply.github.com>
@yushinj
Copy link
Copy Markdown
Collaborator Author

yushinj commented Apr 10, 2026

@copilot apply changes based on the comments in this thread

# Conflicts:
#	my-app/src/app/api/create-profile/route.ts

Co-authored-by: yushinj <149107617+yushinj@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

@copilot apply changes based on the comments in this thread

All six review comments were addressed in commit 4c509e6 (merged into f1b5930): payload merge fix, normalized error matching, admin role preservation, login() URL fix, not_connected-only invite logic, and vendor_status.is.null in the sync-all filter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants