Files
fourge-portal/src/lib/invoiceVersionRules.js
T
Krao Hasanee 8eacd86b04 fix: invoice integrity — atomic numbering, safe delete, single create path
- Invoice numbers now come from DB function next_invoice_number()
  (max+1 per year under advisory lock) with a unique index; the old
  row-count method reused numbers after deletes and raced concurrent
  creates, which broke public pay links
- Remove dead standalone TeamCreateInvoice page; the TeamInvoices
  modal is the single create path (page had already drifted)
- Invoice delete now asks for confirmation and only un-bills tasks/
  submissions not billed on another invoice
- Created invoice shows correct "sent" status in list without reload
- Invoice/due dates computed at save time, not module load
- Reopening a paid invoice clears stale stripe_fee
- Stripe webhook markPaid is idempotent (no double receipts)
- subcontractor_invoice_items.version_number column stores billing
  version explicitly; description parsing kept as legacy fallback
- Drop unused buildInvoiceStatusByKey/deriveVersionStatus

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 10:02:28 -04:00

28 lines
1.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export const CLIENT_APPROVED_OR_BETTER = new Set(['client_approved', 'invoiced', 'paid']);
export function isCompletedVersionEligible(task, versionNumber) {
const taskVersion = Number(task?.current_version || 0);
const version = Number(versionNumber || 0);
if (version < taskVersion) return true;
return version === taskVersion && CLIENT_APPROVED_OR_BETTER.has(task?.status);
}
export function isInitialVersionEligible(task) {
if (!task || task.invoiced) return false;
return isCompletedVersionEligible(task, 0);
}
export function getRevisionChargeQuantity(versionNumber, revisionType) {
// Fourge's own error revisions are never billed to the client.
if (revisionType === 'fourge_error') return 0;
const version = Number(versionNumber || 0);
return version >= 2 ? 1 : 0;
}
// Parses version number from a subcontractor invoice item description like
// "Project • Task R01". Legacy fallback only — new rows store version_number directly.
export function parseVersionFromItemDescription(description = '') {
const match = String(description).match(/[\-]\s*R(\d{2})\b/i);
return match ? Number(match[1]) : 0;
}