fix for createRefund
This commit is contained in:
@@ -900,10 +900,7 @@ async function createExpense(data) {
|
||||
|
||||
|
||||
/**
|
||||
* Erstellt einen QBO Deposit für einen Vendor-Refund (Geld kam zurück aufs Konto).
|
||||
*
|
||||
* Buchhalterisch: Der Refund wird gegen die ursprüngliche Expense-Kategorie
|
||||
* gebucht, wodurch sich der Aufwand in dieser Kategorie reduziert.
|
||||
* Erstellt einen QBO Deposit oder Purchase Credit für einen Vendor-Refund.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {string} data.vendorId - Pflicht (von wem kam der Refund)
|
||||
@@ -953,8 +950,39 @@ async function createRefund(data) {
|
||||
);
|
||||
const vendorName = vendorRow.rows[0]?.display_name || data.vendorId;
|
||||
|
||||
// ── QBO Deposit Payload ──
|
||||
const payload = {
|
||||
const { companyId, baseUrl } = getClientInfo();
|
||||
const isCreditCard = depositAcct.account_type === 'Credit Card';
|
||||
|
||||
let url;
|
||||
let payload;
|
||||
let entityTypeLabel;
|
||||
|
||||
// ── Verzweigung: Credit Card Credit (Purchase) vs. Bank (Deposit) ──
|
||||
if (isCreditCard) {
|
||||
entityTypeLabel = 'Purchase';
|
||||
url = withMinorVersion(`${baseUrl}/v3/company/${companyId}/purchase`);
|
||||
payload = {
|
||||
AccountRef: { value: depositAcct.qbo_id, name: depositAcct.name },
|
||||
EntityRef: { value: data.vendorId, type: 'Vendor', name: vendorName },
|
||||
TxnDate: data.txnDate,
|
||||
PaymentType: 'CreditCard',
|
||||
Credit: true, // Zwingend erforderlich für einen CC Refund
|
||||
Line: [{
|
||||
DetailType: 'AccountBasedExpenseLineDetail',
|
||||
Amount: amount,
|
||||
Description: data.memo || `Refund from ${vendorName}`,
|
||||
AccountBasedExpenseLineDetail: {
|
||||
AccountRef: { value: categoryAcct.qbo_id, name: categoryAcct.name }
|
||||
}
|
||||
}]
|
||||
};
|
||||
if (data.refNo) payload.DocNumber = String(data.refNo).slice(0, 21);
|
||||
if (data.memo) payload.PrivateNote = String(data.memo);
|
||||
|
||||
} else {
|
||||
entityTypeLabel = 'Deposit';
|
||||
url = withMinorVersion(`${baseUrl}/v3/company/${companyId}/deposit`);
|
||||
payload = {
|
||||
DepositToAccountRef: { value: depositAcct.qbo_id, name: depositAcct.name },
|
||||
TxnDate: data.txnDate,
|
||||
Line: [{
|
||||
@@ -963,18 +991,19 @@ async function createRefund(data) {
|
||||
Description: data.memo || `Refund from ${vendorName}`,
|
||||
DepositLineDetail: {
|
||||
AccountRef: { value: categoryAcct.qbo_id, name: categoryAcct.name },
|
||||
Entity: { value: data.vendorId, type: 'Vendor' }
|
||||
// Korrigierte QBO Entity-Struktur
|
||||
Entity: {
|
||||
Type: 'Vendor',
|
||||
EntityRef: { value: data.vendorId, name: vendorName }
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
if (data.refNo) payload.Line[0].DepositLineDetail.CheckNum = String(data.refNo).slice(0, 21);
|
||||
if (data.memo) payload.PrivateNote = String(data.memo);
|
||||
}
|
||||
|
||||
const { companyId, baseUrl } = getClientInfo();
|
||||
const url = withMinorVersion(`${baseUrl}/v3/company/${companyId}/deposit`);
|
||||
|
||||
const requestSummary = `REFUND | ${vendorName} → ${depositAcct.name} | ${categoryAcct.name} | ${data.txnDate} | $${amount.toFixed(2)}`;
|
||||
const requestSummary = `REFUND (${entityTypeLabel}) | ${vendorName} → ${depositAcct.name} | ${categoryAcct.name} | ${data.txnDate} | $${amount.toFixed(2)}`;
|
||||
|
||||
let qboResponse;
|
||||
try {
|
||||
@@ -988,7 +1017,7 @@ async function createRefund(data) {
|
||||
} catch (err) {
|
||||
await writeAuditLog({
|
||||
action: 'refund.create',
|
||||
entityType: 'Deposit',
|
||||
entityType: entityTypeLabel,
|
||||
status: 'error',
|
||||
requestExcerpt: requestSummary,
|
||||
responseExcerpt: err.message
|
||||
@@ -1002,34 +1031,35 @@ async function createRefund(data) {
|
||||
).join('; ');
|
||||
await writeAuditLog({
|
||||
action: 'refund.create',
|
||||
entityType: 'Deposit',
|
||||
entityType: entityTypeLabel,
|
||||
status: 'error',
|
||||
requestExcerpt: requestSummary,
|
||||
responseExcerpt: msg
|
||||
});
|
||||
const err = new Error('QBO Deposit (refund) create failed: ' + msg);
|
||||
const err = new Error(`QBO ${entityTypeLabel} (refund) create failed: ${msg}`);
|
||||
err.qboFault = qboResponse.Fault;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const deposit = qboResponse.Deposit;
|
||||
if (!deposit || !deposit.Id) throw new Error('QBO returned no Deposit id');
|
||||
// Dynamisches Extrahieren basierend auf dem Entity-Typ
|
||||
const resultEntity = isCreditCard ? qboResponse.Purchase : qboResponse.Deposit;
|
||||
if (!resultEntity || !resultEntity.Id) throw new Error(`QBO returned no ${entityTypeLabel} id`);
|
||||
|
||||
await writeAuditLog({
|
||||
action: 'refund.create',
|
||||
entityType: 'Deposit',
|
||||
entityQboId: deposit.Id,
|
||||
entityType: entityTypeLabel,
|
||||
entityQboId: resultEntity.Id,
|
||||
status: 'success',
|
||||
requestExcerpt: requestSummary,
|
||||
responseExcerpt: `Deposit ${deposit.Id} created, total $${Number(deposit.TotalAmt).toFixed(2)}`
|
||||
responseExcerpt: `${entityTypeLabel} ${resultEntity.Id} created, total $${Number(resultEntity.TotalAmt).toFixed(2)}`
|
||||
});
|
||||
|
||||
console.log(`✅ QBO Refund recorded: Deposit ${deposit.Id} — ${requestSummary}`);
|
||||
console.log(`✅ QBO Refund recorded: ${entityTypeLabel} ${resultEntity.Id} — ${requestSummary}`);
|
||||
|
||||
return {
|
||||
id: deposit.Id,
|
||||
txnDate: deposit.TxnDate,
|
||||
totalAmt: Number(deposit.TotalAmt),
|
||||
id: resultEntity.Id,
|
||||
txnDate: resultEntity.TxnDate,
|
||||
totalAmt: Number(resultEntity.TotalAmt),
|
||||
vendorName,
|
||||
depositAccountName: depositAcct.name,
|
||||
categoryName: categoryAcct.name
|
||||
|
||||
Reference in New Issue
Block a user