import { ParsedRFP, CostEstimate, QuoteDocument, QuoteLine } from './types';
/**
* Generate a quote document from RFP and estimate
*/
export function generateQuote(
parsedRfp: ParsedRFP,
estimate: CostEstimate
): QuoteDocument {
const lines: QuoteLine[] = [];
// Main line item
const desc = buildDescription(parsedRfp);
lines.push({
desc,
qty: parsedRfp.qty,
unit: estimate.pricePerUnit,
total: Number((estimate.pricePerUnit * parsedRfp.qty).toFixed(2)),
});
// Add process details if available
if (parsedRfp.processes && parsedRfp.processes.length > 0) {
const processDesc = ` Processes: ${parsedRfp.processes.join(', ')}`;
lines.push({
desc: processDesc,
qty: 0,
unit: 0,
total: 0,
});
}
// Add notes for tolerances
if (parsedRfp.tolerances) {
lines.push({
desc: ` Tolerances: ${parsedRfp.tolerances}`,
qty: 0,
unit: 0,
total: 0,
});
}
// Generate quote ID
const quoteId = generateQuoteId();
// Calculate valid until date (typically 30 days)
const validUntil = new Date();
validUntil.setDate(validUntil.getDate() + 30);
// Build terms
const terms = buildTerms(estimate);
return {
quoteId,
to: parsedRfp.contactEmail || 'unknown',
customerName: parsedRfp.customerName,
lines,
terms,
confidence: estimate.confidence,
validUntil: validUntil.toISOString().split('T')[0],
paymentTerms: 'Net 30',
createdAt: new Date().toISOString(),
status: 'draft',
};
}
/**
* Build item description
*/
function buildDescription(rfp: ParsedRFP): string {
let desc = `Manufacture ${rfp.qty} x `;
if (rfp.partNumber) {
desc += `Part #${rfp.partNumber}`;
} else {
desc += 'custom part';
}
if (rfp.material && rfp.material !== 'UNKNOWN') {
desc += ` (${rfp.material})`;
}
if (rfp.finish) {
desc += `, ${rfp.finish} finish`;
}
return desc;
}
/**
* Build terms section
*/
function buildTerms(estimate: CostEstimate): string {
let terms = `Lead time: ${estimate.leadDays} business days from receipt of order`;
if (estimate.bestCase && estimate.worstCase) {
terms += ` (${estimate.bestCase.leadDays}-${estimate.worstCase.leadDays} days range based on ${estimate.confidence} confidence)`;
}
terms += '. Payment terms: Net 30. ';
terms += 'F.O.B. Origin. ';
if (estimate.confidence === 'low') {
terms += 'NOTE: This is a preliminary estimate. Final pricing subject to review of drawings/specifications. ';
}
terms += 'Quote valid for 30 days.';
return terms;
}
/**
* Generate unique quote ID
*/
function generateQuoteId(): string {
const date = new Date();
const year = date.getFullYear().toString().slice(-2);
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
return `Q${year}${month}${day}-${random}`;
}
/**
* Format quote document as text
*/
export function formatQuoteDocument(doc: QuoteDocument): string {
let output = '';
output += '='.repeat(70) + '\n';
output += `QUOTATION: ${doc.quoteId}\n`;
output += '='.repeat(70) + '\n\n';
output += `Date: ${new Date(doc.createdAt).toLocaleDateString()}\n`;
output += `To: ${doc.to}\n`;
if (doc.customerName) {
output += `Customer: ${doc.customerName}\n`;
}
output += `Valid Until: ${doc.validUntil}\n`;
output += `Status: ${doc.status.toUpperCase()}\n\n`;
output += 'LINE ITEMS:\n';
output += '-'.repeat(70) + '\n';
for (const line of doc.lines) {
if (line.qty === 0) {
// Detail line
output += `${line.desc}\n`;
} else {
// Priced line
output += `${line.desc}\n`;
output += ` Quantity: ${line.qty} @ $${line.unit.toFixed(2)}/ea\n`;
output += ` Total: $${line.total.toFixed(2)}\n`;
}
}
output += '-'.repeat(70) + '\n';
const total = doc.lines.reduce((sum, line) => sum + line.total, 0);
output += `TOTAL: $${total.toFixed(2)}\n\n`;
output += 'TERMS & CONDITIONS:\n';
output += doc.terms + '\n\n';
if (doc.paymentTerms) {
output += `Payment Terms: ${doc.paymentTerms}\n`;
}
if (doc.confidence === 'low' || doc.confidence === 'medium') {
output += '\n';
output += '⚠️ NOTICE: This quote has ' + doc.confidence.toUpperCase() + ' confidence.\n';
output += 'Please review carefully and provide additional details if available.\n';
}
output += '\n';
output += '='.repeat(70) + '\n';
return output;
}
/**
* Format quote for email
*/
export function formatQuoteEmail(doc: QuoteDocument): { subject: string; body: string } {
const subject = `Quotation ${doc.quoteId}${doc.customerName ? ` - ${doc.customerName}` : ''}`;
let body = `Dear Customer,\n\n`;
body += `Thank you for your request for quotation. Please find our quote below:\n\n`;
body += formatQuoteDocument(doc);
body += `\n`;
body += `If you have any questions or would like to proceed with this order, please contact us.\n\n`;
body += `Best regards,\n`;
body += `Sales Team\n`;
return { subject, body };
}