sent,mark sent

This commit is contained in:
2026-02-24 17:13:27 -06:00
parent ec3cd2b659
commit 73b869e2d9
2 changed files with 95 additions and 4 deletions

View File

@@ -177,7 +177,7 @@ async function exportInvoiceToQbo(invoiceId, client) {
"TxnDate": invoice.invoice_date.toISOString().split('T')[0],
"Line": lineItems,
"CustomerMemo": { "value": invoice.auth_code ? `Auth: ${invoice.auth_code}` : "" },
"EmailStatus": "EmailSent",
"EmailStatus": "NotSet",
"BillEmail": { "Address": invoice.email || "" }
};
@@ -292,7 +292,6 @@ async function syncInvoiceToQbo(invoiceId, client) {
}
// Logo endpoints
app.get('/api/logo-info', async (req, res) => {
try {
@@ -833,6 +832,7 @@ app.post('/api/invoices', async (req, res) => {
}
});
app.post('/api/quotes/:id/convert-to-invoice', async (req, res) => {
const { id } = req.params;
@@ -1062,6 +1062,69 @@ app.delete('/api/invoices/:id', async (req, res) => {
}
});
app.patch('/api/invoices/:id/email-status', async (req, res) => {
const { id } = req.params;
const { status } = req.body; // 'sent' or 'open'
if (!['sent', 'open'].includes(status)) {
return res.status(400).json({ error: 'Status must be "sent" or "open".' });
}
try {
const invResult = await pool.query('SELECT qbo_id FROM invoices WHERE id = $1', [id]);
if (invResult.rows.length === 0) return res.status(404).json({ error: 'Invoice not found' });
const invoice = invResult.rows[0];
// QBO updaten falls vorhanden
if (invoice.qbo_id) {
const oauthClient = getOAuthClient();
const companyId = oauthClient.getToken().realmId;
const baseUrl = process.env.QBO_ENVIRONMENT === 'production'
? 'https://quickbooks.api.intuit.com'
: 'https://sandbox-quickbooks.api.intuit.com';
// SyncToken holen
const qboRes = await makeQboApiCall({
url: `${baseUrl}/v3/company/${companyId}/invoice/${invoice.qbo_id}`,
method: 'GET'
});
const qboData = qboRes.getJson ? qboRes.getJson() : qboRes.json;
const syncToken = qboData.Invoice?.SyncToken;
if (syncToken !== undefined) {
const emailStatus = status === 'sent' ? 'EmailSent' : 'NotSet';
await makeQboApiCall({
url: `${baseUrl}/v3/company/${companyId}/invoice`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
Id: invoice.qbo_id,
SyncToken: syncToken,
sparse: true,
EmailStatus: emailStatus
})
});
console.log(`✅ QBO Invoice ${invoice.qbo_id} email status → ${emailStatus}`);
}
}
// Lokal updaten
await pool.query(
'UPDATE invoices SET email_status = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2',
[status, id]
);
res.json({ success: true, status });
} catch (error) {
console.error('Error updating email status:', error);
res.status(500).json({ error: 'Failed to update status: ' + error.message });
}
});
// PDF Generation code continues below...
// PDF Generation using templates and persistent browser