feat(260519-0oz-01): add QboPaymentCreatePayload, QboDepositCreatePayload types and createPayment/createDeposit methods to QboClient
- Add QboPaymentCreatePayload + QboDepositCreatePayload interfaces to lib/types/qbo.ts - Add createPayment(payload) and createDeposit(payload) public methods to QboClient - Both methods use existing private this.request<T>() with POST + minorversion=65 - Both methods throw descriptively if QBO returns no Id in response
This commit is contained in:
parent
5f4ccb9c56
commit
ef9b31e7c2
2 changed files with 93 additions and 0 deletions
|
|
@ -14,6 +14,8 @@ import {
|
||||||
QboJournalEntry,
|
QboJournalEntry,
|
||||||
QboReport,
|
QboReport,
|
||||||
QboQueryResponse,
|
QboQueryResponse,
|
||||||
|
QboPaymentCreatePayload,
|
||||||
|
QboDepositCreatePayload,
|
||||||
} from '@/lib/types/qbo';
|
} from '@/lib/types/qbo';
|
||||||
|
|
||||||
const QBO_PRODUCTION_URL = 'https://quickbooks.api.intuit.com';
|
const QBO_PRODUCTION_URL = 'https://quickbooks.api.intuit.com';
|
||||||
|
|
@ -225,6 +227,45 @@ export class QboClient {
|
||||||
return this.queryAll<QboDeposit>('Deposit', where);
|
return this.queryAll<QboDeposit>('Deposit', where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Entity Creators ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Receive Payment in QBO and apply it to one or more invoices.
|
||||||
|
* Returns the created Payment as QBO echoes it back (includes Id, SyncToken).
|
||||||
|
*/
|
||||||
|
async createPayment(payload: QboPaymentCreatePayload): Promise<QboPayment> {
|
||||||
|
const data = await this.request<{ Payment: QboPayment }>(
|
||||||
|
`/payment?minorversion=65`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (!data?.Payment?.Id) {
|
||||||
|
throw new Error(`QBO createPayment returned no Payment: ${JSON.stringify(data).slice(0, 500)}`);
|
||||||
|
}
|
||||||
|
return data.Payment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Deposit in QBO that groups one or more existing Payments into a
|
||||||
|
* single bank-deposit line (so QBO's bank-feed reconciliation matches the
|
||||||
|
* bank's actual deposit slip).
|
||||||
|
*/
|
||||||
|
async createDeposit(payload: QboDepositCreatePayload): Promise<QboDeposit> {
|
||||||
|
const data = await this.request<{ Deposit: QboDeposit }>(
|
||||||
|
`/deposit?minorversion=65`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (!data?.Deposit?.Id) {
|
||||||
|
throw new Error(`QBO createDeposit returned no Deposit: ${JSON.stringify(data).slice(0, 500)}`);
|
||||||
|
}
|
||||||
|
return data.Deposit;
|
||||||
|
}
|
||||||
|
|
||||||
async getPurchases(updatedSince?: Date): Promise<QboPurchase[]> {
|
async getPurchases(updatedSince?: Date): Promise<QboPurchase[]> {
|
||||||
const where = updatedSince
|
const where = updatedSince
|
||||||
? `MetaData.LastUpdatedTime > '${updatedSince.toISOString()}'`
|
? `MetaData.LastUpdatedTime > '${updatedSince.toISOString()}'`
|
||||||
|
|
@ -278,6 +319,26 @@ export class QboClient {
|
||||||
});
|
});
|
||||||
return this.request<QboReport>(`/reports/CashFlow?${params.toString()}`);
|
return this.request<QboReport>(`/reports/CashFlow?${params.toString()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A/R Aging Detail — one row per open invoice, with customer + balance + days
|
||||||
|
// past due. Used to reconcile Pulse's qbo_invoices against QBO's view of A/R.
|
||||||
|
async getAgedReceivableDetail(asOfDate?: string): Promise<QboReport> {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
report_date: asOfDate ?? new Date().toISOString().slice(0, 10),
|
||||||
|
aging_method: 'Report_Date',
|
||||||
|
minorversion: '65',
|
||||||
|
});
|
||||||
|
return this.request<QboReport>(`/reports/AgedReceivableDetail?${params.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAgedReceivableSummary(asOfDate?: string): Promise<QboReport> {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
report_date: asOfDate ?? new Date().toISOString().slice(0, 10),
|
||||||
|
aging_method: 'Report_Date',
|
||||||
|
minorversion: '65',
|
||||||
|
});
|
||||||
|
return this.request<QboReport>(`/reports/AgedReceivables?${params.toString()}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _instance: QboClient | null = null;
|
let _instance: QboClient | null = null;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,37 @@ export interface QboDeposit {
|
||||||
MetaData?: QboMetaData;
|
MetaData?: QboMetaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Payload for creating a Payment via POST /v3/company/{realmId}/payment
|
||||||
|
// Only the fields we actually send — QBO accepts many more but we keep the
|
||||||
|
// surface area small and explicit.
|
||||||
|
export interface QboPaymentCreatePayload {
|
||||||
|
CustomerRef: QboRef; // { value: customer_ref_id }
|
||||||
|
TotalAmt: number;
|
||||||
|
TxnDate?: string; // YYYY-MM-DD
|
||||||
|
DepositToAccountRef?: QboRef; // { value: account id }
|
||||||
|
PaymentRefNum?: string; // check number (max 21 chars per QBO)
|
||||||
|
PrivateNote?: string;
|
||||||
|
Line?: Array<{
|
||||||
|
Amount: number;
|
||||||
|
LinkedTxn?: QboLinkedTxn[]; // one entry per invoice being paid
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload for creating a Deposit via POST /v3/company/{realmId}/deposit
|
||||||
|
// Each Line links to a previously-created Payment so QBO groups them as a
|
||||||
|
// single bank deposit (matches the bank slip).
|
||||||
|
export interface QboDepositCreatePayload {
|
||||||
|
TxnDate?: string;
|
||||||
|
DepositToAccountRef: QboRef; // required for deposit
|
||||||
|
PrivateNote?: string;
|
||||||
|
Line: Array<{
|
||||||
|
Amount: number;
|
||||||
|
DetailType: 'DepositLineDetail';
|
||||||
|
LinkedTxn?: QboLinkedTxn[]; // [{ TxnId: payment_id, TxnType: 'Payment' }]
|
||||||
|
DepositLineDetail?: Record<string, unknown>;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
// Purchase (expense/credit card)
|
// Purchase (expense/credit card)
|
||||||
export interface QboPurchase {
|
export interface QboPurchase {
|
||||||
Id: string;
|
Id: string;
|
||||||
|
|
@ -156,6 +187,7 @@ export interface QboEntitySyncResult {
|
||||||
entity: string;
|
entity: string;
|
||||||
success: boolean;
|
success: boolean;
|
||||||
recordsUpserted: number;
|
recordsUpserted: number;
|
||||||
|
recordsDeleted?: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue