From 198126c13e22aae6cd8da3862fd6ff12c0c16ce8 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Sat, 28 Feb 2026 15:01:44 -0600 Subject: [PATCH] schema + auth --- .gitignore | 2 +- auth.js | 67 ++++++++ schema.sql | 453 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 auth.js create mode 100644 schema.sql diff --git a/.gitignore b/.gitignore index 83563e6..fb4ec11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .env -*.png +public/uploads/*.png node_modules qbo_token.json \ No newline at end of file diff --git a/auth.js b/auth.js new file mode 100644 index 0000000..0104aa6 --- /dev/null +++ b/auth.js @@ -0,0 +1,67 @@ +const OAuthClient = require('intuit-oauth'); +const express = require('express'); + +const app = express(); + +// 1. Konfiguration (Füge hier deine Development Keys ein) + +const oauthClient = new OAuthClient({ + clientId: process.env.QBO_CLIENT_ID, + clientSecret: process.env.QBO_CLIENT_SECRET, + environment: process.env.QBO_ENVIRONMENT, // Wichtig: 'sandbox' für Development Keys + redirectUri: process.env.QBO_REDIRECT_URI, +}); +// 2. Start-Route: Generiert die Login-URL und leitet dich weiter +app.get('/', (req, res) => { + const authUri = oauthClient.authorizeUri({ + scope: [OAuthClient.scopes.Accounting, OAuthClient.scopes.Payment], + state: 'testState', + }); + + console.log('Öffne Browser für Login...'); + res.redirect(authUri); +}); + +// 3. Callback-Route: Hierhin kommt QBO zurück mit dem Code +app.get('/callback', async (req, res) => { + try { + // 1. Tokens holen + const authResponse = await oauthClient.createToken(req.url); + const tokens = authResponse.getJson(); + const realmId = authResponse.token.realmId; + + // 2. Test-Abruf (Kunden) + const url = oauthClient.environment == 'sandbox' + ? OAuthClient.environment.sandbox + : OAuthClient.environment.production; + + const apiResponse = await oauthClient.makeApiCall({ + url: `${url}v3/company/${realmId}/query?query=select * from Customer MAXRESULTS 5`, + method: 'GET', + }); + + // 3. Ausgabe in der Konsole + console.log('\n--- DEINE TOKENS (BITTE SICHERN) ---'); + console.log('Realm ID:', realmId); + console.log('Access Token:', tokens.access_token); + console.log('Refresh Token:', tokens.refresh_token); + console.log('------------------------------------\n'); + + console.log("Test-Abruf Ergebnis:"); + // KORREKTUR: .getJson() statt .text() + console.log(JSON.stringify(apiResponse.getJson(), null, 2)); + + // 4. Antwort an Browser (Erst ganz am Ende senden!) + res.send(`

Erfolg!

Tokens sind in der Konsole.

`); + + } catch (e) { + console.error("Ein Fehler ist aufgetreten:", e); + // Nur senden, wenn noch nichts gesendet wurde + if (!res.headersSent) res.send('Fehler: Siehe Konsole'); + } +}); + +// Server starten +app.listen(3000, async () => { + console.log('Server läuft auf http://localhost:3000'); +}); \ No newline at end of file diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..6014d6a --- /dev/null +++ b/schema.sql @@ -0,0 +1,453 @@ +-- +-- PostgreSQL database dump +-- + +\restrict XHJaQEVNwjEtL1FZTBb0Sf7ooBX1Ld95BOqQlHUgJxKe87sxBoQbgpWG7aympDU + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: customers; Type: TABLE; Schema: public; Owner: quoteuser +-- + +CREATE TABLE public.customers ( + id integer NOT NULL, + name character varying(255) NOT NULL, + city character varying(100), + state character varying(2), + zip_code character varying(10), + account_number character varying(50), + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + email character varying(255), + phone character varying(50), + phone2 character varying(50), + taxable boolean DEFAULT true, + line1 character varying(255), + line2 character varying(255), + line3 character varying(255), + line4 character varying(255), + qbo_id character varying(50), + qbo_sync_token character varying(50) +); + + +ALTER TABLE public.customers OWNER TO quoteuser; + +-- +-- Name: customers_id_seq; Type: SEQUENCE; Schema: public; Owner: quoteuser +-- + +CREATE SEQUENCE public.customers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.customers_id_seq OWNER TO quoteuser; + +-- +-- Name: customers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: quoteuser +-- + +ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id; + + +-- +-- Name: invoice_items; Type: TABLE; Schema: public; Owner: quoteuser +-- + +CREATE TABLE public.invoice_items ( + id integer NOT NULL, + invoice_id integer, + quantity character varying(20) NOT NULL, + description text NOT NULL, + rate character varying(50) NOT NULL, + amount character varying(50) NOT NULL, + item_order integer NOT NULL, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + qbo_item_id character varying(10) DEFAULT '9'::character varying +); + + +ALTER TABLE public.invoice_items OWNER TO quoteuser; + +-- +-- Name: invoice_items_id_seq; Type: SEQUENCE; Schema: public; Owner: quoteuser +-- + +CREATE SEQUENCE public.invoice_items_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.invoice_items_id_seq OWNER TO quoteuser; + +-- +-- Name: invoice_items_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: quoteuser +-- + +ALTER SEQUENCE public.invoice_items_id_seq OWNED BY public.invoice_items.id; + + +-- +-- Name: invoices; Type: TABLE; Schema: public; Owner: quoteuser +-- + +CREATE TABLE public.invoices ( + id integer NOT NULL, + invoice_number character varying(50) NOT NULL, + customer_id integer, + invoice_date date NOT NULL, + terms character varying(100) DEFAULT 'Net 30'::character varying, + auth_code character varying(255), + tax_exempt boolean DEFAULT false, + tax_rate numeric(5,2) DEFAULT 8.25, + subtotal numeric(10,2) DEFAULT 0, + tax_amount numeric(10,2) DEFAULT 0, + total numeric(10,2) DEFAULT 0, + created_from_quote_id integer, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + qbo_id character varying(50), + qbo_sync_token character varying(50), + qbo_doc_number character varying(50) +); + + +ALTER TABLE public.invoices OWNER TO quoteuser; + +-- +-- Name: invoices_id_seq; Type: SEQUENCE; Schema: public; Owner: quoteuser +-- + +CREATE SEQUENCE public.invoices_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.invoices_id_seq OWNER TO quoteuser; + +-- +-- Name: invoices_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: quoteuser +-- + +ALTER SEQUENCE public.invoices_id_seq OWNED BY public.invoices.id; + + +-- +-- Name: quote_items; Type: TABLE; Schema: public; Owner: quoteuser +-- + +CREATE TABLE public.quote_items ( + id integer NOT NULL, + quote_id integer, + quantity character varying(20) NOT NULL, + description text NOT NULL, + rate character varying(50) NOT NULL, + amount character varying(50) NOT NULL, + item_order integer NOT NULL, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + is_tbd boolean DEFAULT false, + qbo_item_id character varying(10) DEFAULT '9'::character varying +); + + +ALTER TABLE public.quote_items OWNER TO quoteuser; + +-- +-- Name: quote_items_id_seq; Type: SEQUENCE; Schema: public; Owner: quoteuser +-- + +CREATE SEQUENCE public.quote_items_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.quote_items_id_seq OWNER TO quoteuser; + +-- +-- Name: quote_items_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: quoteuser +-- + +ALTER SEQUENCE public.quote_items_id_seq OWNED BY public.quote_items.id; + + +-- +-- Name: quotes; Type: TABLE; Schema: public; Owner: quoteuser +-- + +CREATE TABLE public.quotes ( + id integer NOT NULL, + quote_number character varying(50) NOT NULL, + customer_id integer, + quote_date date NOT NULL, + tax_exempt boolean DEFAULT false, + tax_rate numeric(5,2) DEFAULT 8.25, + subtotal numeric(10,2) DEFAULT 0, + tax_amount numeric(10,2) DEFAULT 0, + total numeric(10,2) DEFAULT 0, + has_tbd boolean DEFAULT false, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + tbd_note text +); + + +ALTER TABLE public.quotes OWNER TO quoteuser; + +-- +-- Name: quotes_id_seq; Type: SEQUENCE; Schema: public; Owner: quoteuser +-- + +CREATE SEQUENCE public.quotes_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.quotes_id_seq OWNER TO quoteuser; + +-- +-- Name: quotes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: quoteuser +-- + +ALTER SEQUENCE public.quotes_id_seq OWNED BY public.quotes.id; + + +-- +-- Name: customers id; Type: DEFAULT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass); + + +-- +-- Name: invoice_items id; Type: DEFAULT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoice_items ALTER COLUMN id SET DEFAULT nextval('public.invoice_items_id_seq'::regclass); + + +-- +-- Name: invoices id; Type: DEFAULT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoices ALTER COLUMN id SET DEFAULT nextval('public.invoices_id_seq'::regclass); + + +-- +-- Name: quote_items id; Type: DEFAULT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quote_items ALTER COLUMN id SET DEFAULT nextval('public.quote_items_id_seq'::regclass); + + +-- +-- Name: quotes id; Type: DEFAULT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quotes ALTER COLUMN id SET DEFAULT nextval('public.quotes_id_seq'::regclass); + + +-- +-- Name: customers customers_pkey; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT customers_pkey PRIMARY KEY (id); + + +-- +-- Name: customers customers_qbo_id_key; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT customers_qbo_id_key UNIQUE (qbo_id); + + +-- +-- Name: invoice_items invoice_items_pkey; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoice_items + ADD CONSTRAINT invoice_items_pkey PRIMARY KEY (id); + + +-- +-- Name: invoices invoices_invoice_number_key; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT invoices_invoice_number_key UNIQUE (invoice_number); + + +-- +-- Name: invoices invoices_pkey; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT invoices_pkey PRIMARY KEY (id); + + +-- +-- Name: quote_items quote_items_pkey; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quote_items + ADD CONSTRAINT quote_items_pkey PRIMARY KEY (id); + + +-- +-- Name: quotes quotes_pkey; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quotes + ADD CONSTRAINT quotes_pkey PRIMARY KEY (id); + + +-- +-- Name: quotes quotes_quote_number_key; Type: CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quotes + ADD CONSTRAINT quotes_quote_number_key UNIQUE (quote_number); + + +-- +-- Name: idx_customers_qbo_id; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE UNIQUE INDEX idx_customers_qbo_id ON public.customers USING btree (qbo_id); + + +-- +-- Name: idx_invoice_items_invoice_id; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_invoice_items_invoice_id ON public.invoice_items USING btree (invoice_id); + + +-- +-- Name: idx_invoices_created_from_quote; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_invoices_created_from_quote ON public.invoices USING btree (created_from_quote_id); + + +-- +-- Name: idx_invoices_customer_id; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_invoices_customer_id ON public.invoices USING btree (customer_id); + + +-- +-- Name: idx_invoices_invoice_number; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_invoices_invoice_number ON public.invoices USING btree (invoice_number); + + +-- +-- Name: idx_quote_items_quote_id; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_quote_items_quote_id ON public.quote_items USING btree (quote_id); + + +-- +-- Name: idx_quotes_customer_id; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_quotes_customer_id ON public.quotes USING btree (customer_id); + + +-- +-- Name: idx_quotes_quote_number; Type: INDEX; Schema: public; Owner: quoteuser +-- + +CREATE INDEX idx_quotes_quote_number ON public.quotes USING btree (quote_number); + + +-- +-- Name: invoice_items invoice_items_invoice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoice_items + ADD CONSTRAINT invoice_items_invoice_id_fkey FOREIGN KEY (invoice_id) REFERENCES public.invoices(id) ON DELETE CASCADE; + + +-- +-- Name: invoices invoices_created_from_quote_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT invoices_created_from_quote_id_fkey FOREIGN KEY (created_from_quote_id) REFERENCES public.quotes(id); + + +-- +-- Name: invoices invoices_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT invoices_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customers(id); + + +-- +-- Name: quote_items quote_items_quote_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quote_items + ADD CONSTRAINT quote_items_quote_id_fkey FOREIGN KEY (quote_id) REFERENCES public.quotes(id) ON DELETE CASCADE; + + +-- +-- Name: quotes quotes_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: quoteuser +-- + +ALTER TABLE ONLY public.quotes + ADD CONSTRAINT quotes_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customers(id); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict XHJaQEVNwjEtL1FZTBb0Sf7ooBX1Ld95BOqQlHUgJxKe87sxBoQbgpWG7aympDU +