update
This commit is contained in:
157
import_customers_qbo.js
Normal file
157
import_customers_qbo.js
Normal file
@@ -0,0 +1,157 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user