157 lines
6.2 KiB
JavaScript
157 lines
6.2 KiB
JavaScript
require('dotenv').config();
|
|
const OAuthClient = require('intuit-oauth');
|
|
const { Client } = require('pg');
|
|
|
|
// --- KONFIGURATION ---
|
|
const totalLimit = null;
|
|
// ---------------------
|
|
|
|
const config = {
|
|
clientId: process.env.QBO_CLIENT_ID,
|
|
clientSecret: process.env.QBO_CLIENT_SECRET,
|
|
environment: process.env.QBO_ENVIRONMENT || 'sandbox',
|
|
redirectUri: process.env.QBO_REDIRECT_URI,
|
|
token: {
|
|
// Wir brauchen initial nur den Refresh Token, Access holen wir uns neu
|
|
access_token: process.env.QBO_ACCESS_TOKEN,
|
|
refresh_token: process.env.QBO_REFRESH_TOKEN,
|
|
realmId: process.env.QBO_REALM_ID
|
|
}
|
|
};
|
|
|
|
// SPEZIAL-CONFIG FÜR LOKALEN ZUGRIFF AUF DOCKER DB
|
|
const dbConfig = {
|
|
user: process.env.DB_USER,
|
|
// WICHTIG: Lokal ist es immer localhost
|
|
host: 'localhost',
|
|
database: process.env.DB_NAME,
|
|
password: process.env.DB_PASSWORD,
|
|
// WICHTIG: Laut deinem docker-compose mapst du 5433 auf 5432!
|
|
port: 5433,
|
|
};
|
|
|
|
async function importCustomers() {
|
|
const oauthClient = new OAuthClient(config);
|
|
const pgClient = new Client(dbConfig);
|
|
|
|
try {
|
|
// console.log("🔄 1. Versuche Token zu erneuern...");
|
|
// try {
|
|
// // Token Refresh erzwingen bevor wir starten
|
|
// const authResponse = await oauthClient.refresh();
|
|
// console.log("✅ Token erfolgreich erneuert!");
|
|
// // Optional: Das neue Token in der Session speichern, falls nötig
|
|
// } catch (tokenErr) {
|
|
// console.error("❌ Token Refresh fehlgeschlagen. Prüfe QBO_REFRESH_TOKEN in .env");
|
|
// console.error(tokenErr.originalMessage || tokenErr);
|
|
// return; // Abbruch
|
|
// }
|
|
|
|
console.log(`🔌 2. Verbinde zur DB (Port ${dbConfig.port})...`);
|
|
await pgClient.connect();
|
|
console.log(`✅ DB Verbunden.`);
|
|
|
|
// --- AB HIER DER NORMALE IMPORT ---
|
|
let startPosition = 1;
|
|
let totalProcessed = 0;
|
|
let hasMore = true;
|
|
|
|
while (hasMore) {
|
|
let limitForThisBatch = 100;
|
|
if (totalLimit) {
|
|
const remaining = totalLimit - totalProcessed;
|
|
if (remaining <= 0) break;
|
|
limitForThisBatch = Math.min(100, remaining);
|
|
}
|
|
|
|
const query = `SELECT * FROM Customer STARTPOSITION ${startPosition} MAXRESULTS ${limitForThisBatch}`;
|
|
console.log(`📡 QBO Request: Hole ${limitForThisBatch} Kunden ab Pos ${startPosition}...`);
|
|
|
|
const baseUrl = config.environment === 'production'
|
|
? 'https://quickbooks.api.intuit.com/'
|
|
: 'https://sandbox-quickbooks.api.intuit.com/';
|
|
|
|
const response = await oauthClient.makeApiCall({
|
|
url: `${baseUrl}v3/company/${config.token.realmId}/query?query=${encodeURI(query)}`,
|
|
method: 'GET',
|
|
});
|
|
|
|
const data = response.getJson ? response.getJson() : response.json;
|
|
const customers = data.QueryResponse?.Customer || [];
|
|
|
|
console.log(`📥 QBO Response: ${customers.length} Kunden erhalten.`);
|
|
|
|
if (customers.length === 0) {
|
|
hasMore = false;
|
|
break;
|
|
}
|
|
|
|
for (const c of customers) {
|
|
try {
|
|
const rawPhone = c.PrimaryPhone?.FreeFormNumber || "";
|
|
const formattedAccountNumber = rawPhone.replace(/\D/g, "");
|
|
|
|
const sql = `
|
|
INSERT INTO customers (
|
|
name, line1, line2, line3, line4, city, state, zip_code,
|
|
account_number, email, phone, phone2, taxable, qbo_id, qbo_sync_token, updated_at
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW())
|
|
ON CONFLICT (qbo_id) DO UPDATE SET
|
|
name = EXCLUDED.name,
|
|
line1 = EXCLUDED.line1,
|
|
line2 = EXCLUDED.line2,
|
|
line3 = EXCLUDED.line3,
|
|
line4 = EXCLUDED.line4,
|
|
city = EXCLUDED.city,
|
|
state = EXCLUDED.state,
|
|
zip_code = EXCLUDED.zip_code,
|
|
email = EXCLUDED.email,
|
|
phone = EXCLUDED.phone,
|
|
phone2 = EXCLUDED.phone2,
|
|
qbo_sync_token = EXCLUDED.qbo_sync_token,
|
|
taxable = EXCLUDED.taxable,
|
|
updated_at = NOW();
|
|
`;
|
|
|
|
const values = [
|
|
c.CompanyName || c.DisplayName,
|
|
c.BillAddr?.Line1 || null,
|
|
c.BillAddr?.Line2 || null,
|
|
c.BillAddr?.Line3 || null,
|
|
c.BillAddr?.Line4 || null,
|
|
c.BillAddr?.City || null,
|
|
c.BillAddr?.CountrySubDivisionCode || null,
|
|
c.BillAddr?.PostalCode || null,
|
|
formattedAccountNumber || null,
|
|
c.PrimaryEmailAddr?.Address || null,
|
|
c.PrimaryPhone?.FreeFormNumber || null,
|
|
c.AlternatePhone?.FreeFormNumber || null,
|
|
c.Taxable || false,
|
|
c.Id,
|
|
c.SyncToken
|
|
];
|
|
|
|
await pgClient.query(sql, values);
|
|
totalProcessed++;
|
|
process.stdout.write(".");
|
|
} catch (rowError) {
|
|
console.error(`\n❌ DB Fehler bei Kunde ID ${c.Id}:`, rowError.message);
|
|
}
|
|
}
|
|
console.log("");
|
|
|
|
if (customers.length < limitForThisBatch) hasMore = false;
|
|
startPosition += customers.length;
|
|
}
|
|
|
|
console.log(`\n🎉 Fertig! ${totalProcessed} Kunden verarbeitet.`);
|
|
|
|
} catch (e) {
|
|
console.error("\n💀 FATAL ERROR:", e.message);
|
|
if(e.authResponse) console.log(JSON.stringify(e.authResponse, null, 2));
|
|
} finally {
|
|
await pgClient.end();
|
|
}
|
|
}
|
|
|
|
importCustomers(); |