This commit is contained in:
oliver committed 2026-10-12 10:09:44 -03:00
1 parent 1ea3de74f2
commit 9db080faea
8 files changed
+2064 -55

No files matched your search

+415
View File
@@ -0,0 +1,415 @@
-- =============================================================================
-- Alle Rechnungen pro Monat – Gesamt (ODOO + AMAZON)
-- Odoo 17 / PostgreSQL
--
-- Zweck:
-- Liefert dieselben Spalten wie Rechnungsexport.sql (ODOO-Kundenrechnungen),
-- aber zusammengefuehrt (UNION ALL) mit den Datensaetzen aus der Amazon-
-- Tabelle brandize_datacenter_amazon_vat_transaction. Zusaetzlich gibt die
-- Spalte "Quelle" an, aus welchem System die Zeile stammt ('ODOO'/'AMAZON').
--
-- Modelle:
-- ODOO : account.move (Kundenrechnungen / Gutschriften)
-- AMAZON : brandize_datacenter_amazon_vat_transaction
--
-- Parameter:
-- %(month)s Abrechnungszeitraum als Text, z. B. '2026-SEP'
-- ODOO : Rechnungsdatum im Kalendermonat (siehe params-CTE)
-- AMAZON: activity_period = %(month)s
--
-- Aufruf (Odoo / psycopg2):
-- cr.execute(open('Alle_Rechnungen.sql').read(), {'month': '2026-SEP'})
--
-- In psql stattdessen:
-- \set month '2026-SEP'
--
-- ---------------------------------------------------------------------------
-- Spalten-Mapping ODOO -> AMAZON
-- (Felder ohne Amazon-Pendant bleiben leer)
-- ---------------------------------------------------------------------------
-- Rechnungsdatum <- transaction_complete_date (Fallback: depart)
-- Rechnungsnummer <- vat_inv_number
-- Rechnungsland <- Name zu sale_arrival_country (res_country)
-- Rechnungsland Code <- sale_arrival_country
-- Zahlart <- '' (kein Amazon-Feld)
-- Zahlungsreference <- transaction_event_id
-- USt. Id Kaeufer <- buyer_vat_number
-- Gesamtpreis brutto <- total_activity_value_amt_vat_incl
-- Gesamtpreis netto <- total_activity_value_amt_vat_excl
-- Brutto / -erm <- Steuersplit nach effektivem Satz (Schwelle
-- USt. Satz / -erm CH >= 8, sonst >= 16 - wie im ODOO-Export)
-- Netto Betrag / -erm Satz = total_activity_value_vat_amt / netto * 100
-- Versandkosten brutto <- total_ship_charge_amt_vat_incl
-- Versandkosten netto <- total_ship_charge_amt_vat_excl
-- Auftragsdatum <- transaction_depart_date
-- Bestellnummer <- activity_transaction_id
-- Zahldatum <- '' (kein Amazon-Feld)
-- Lieferadresse Name <- buyer_name
-- Tag <- '' (kein Amazon-Feld)
-- Lieferland <- Name zu arrival_country (res_country)
--
-- ACHTUNG: Das Amazon-Mapping ist eine 1:1-Uebertragung auf die ODOO-Spalten.
-- Die Datumszuordnung (Rechnungsdatum/Auftragsdatum) sowie Bestellnummer
-- sollten gegen die tatsaechliche Amazon-Report-Semantik geprueft und hier
-- ggf. angepasst werden.
-- =============================================================================
WITH params AS (
-- Kalendermonat aus %(month)s (z. B. '2026-SEP') ableiten (ODDO-Seite).
SELECT
to_date(
split_part(%(month)s, '-', 1) || '-' ||
CASE upper(split_part(%(month)s, '-', 2))
WHEN 'JAN' THEN '01' WHEN 'FEB' THEN '02'
WHEN 'MÄR' THEN '03' WHEN 'MAR' THEN '03' WHEN 'APR' THEN '04'
WHEN 'MAI' THEN '05' WHEN 'MAY' THEN '05' WHEN 'JUN' THEN '06'
WHEN 'JUL' THEN '07' WHEN 'AUG' THEN '08' WHEN 'SEP' THEN '09'
WHEN 'OKT' THEN '10' WHEN 'OCT' THEN '10' WHEN 'NOV' THEN '11'
WHEN 'DEZ' THEN '12' WHEN 'DEC' THEN '12'
END,
'YYYY-MM'
) AS first_day
),
-- ===========================================================================
-- ODOO-Seite (identisch zu Rechnungsexport.sql)
-- ===========================================================================
invoices AS (
SELECT
am.id,
am.name,
am.ref,
am.move_type,
am.invoice_date,
am.invoice_origin,
am.partner_id,
am.partner_shipping_id,
am.invoice_payment_term_id,
am.payment_reference
FROM account_move am
CROSS JOIN params p
WHERE am.invoice_date >= p.first_day
AND am.invoice_date < p.first_day + INTERVAL '1 month'
AND am.move_type IN ('out_invoice', 'out_refund')
),
line_taxes AS (
SELECT
aml.move_id AS invoice_id,
at.amount AS tax_rate,
SUM(aml.price_subtotal) AS netto,
SUM(aml.price_total) AS brutto
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN account_move_line_account_tax_rel amlatr
ON amlatr.account_move_line_id = aml.id
JOIN account_tax at
ON at.id = amlatr.account_tax_id
GROUP BY aml.move_id, at.amount
),
tax_split AS (
SELECT
lt.invoice_id,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.brutto::numeric, 2)
END) AS brutto_normal,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.tax_rate::numeric, 2)
END) AS ust_satz_normal,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.netto::numeric, 2)
END) AS netto_normal,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.brutto::numeric, 2)
END) AS brutto_erm,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.tax_rate::numeric, 2)
END) AS ust_satz_erm,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.netto::numeric, 2)
END) AS netto_erm
FROM line_taxes lt
JOIN invoices inv
ON inv.id = lt.invoice_id
JOIN res_partner rp
ON rp.id = inv.partner_id
LEFT JOIN res_country rc
ON rc.id = rp.country_id
GROUP BY lt.invoice_id
),
shipping AS (
SELECT
aml.move_id AS invoice_id,
ROUND(SUM(aml.price_total)::numeric, 2) AS shipping_brutto,
ROUND(SUM(aml.price_subtotal)::numeric, 2) AS shipping_netto
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN product_product pp
ON pp.id = aml.product_id
JOIN product_template pt
ON pt.id = pp.product_tmpl_id
WHERE pt.categ_id = 9
GROUP BY aml.move_id
),
invoice_tags AS (
SELECT
inv.id AS invoice_id,
STRING_AGG(
DISTINCT COALESCE(ct.name->>'en_US', ct.name->>'de_DE', ''),
', ' ORDER BY COALESCE(ct.name->>'en_US', ct.name->>'de_DE', '')
) AS tags
FROM invoices inv
JOIN sale_order so
ON so.name = inv.invoice_origin
JOIN sale_order_tag_rel sotr
ON sotr.order_id = so.id
JOIN crm_tag ct
ON ct.id = sotr.tag_id
GROUP BY inv.id
),
invoice_transaction_reference AS (
SELECT
rel.invoice_id,
pt.provider_reference,
ROW_NUMBER() OVER (
PARTITION BY rel.invoice_id
ORDER BY pt.id
) AS rn
FROM account_invoice_transaction_rel rel
JOIN payment_transaction pt
ON pt.id = rel.transaction_id
),
sale_info AS (
SELECT
inv.id AS invoice_id,
MIN(so.date_order) AS order_date
FROM invoices inv
JOIN sale_order so
ON so.name = inv.invoice_origin
GROUP BY inv.id
),
invoice_payments AS (
SELECT
aml.move_id AS invoice_id,
MAX(apr.max_date) AS payment_date
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN account_account aa
ON aa.id = aml.account_id
AND aa.account_type = 'asset_receivable'
JOIN account_partial_reconcile apr
ON apr.debit_move_id = aml.id
OR apr.credit_move_id = aml.id
GROUP BY aml.move_id
),
odoo_rows AS (
SELECT
TO_CHAR(inv.invoice_date, 'YYYY-MM-DD') AS "Rechnungsdatum",
CASE
WHEN inv.move_type = 'out_refund'
THEN COALESCE(inv.name, inv.ref, '') || '-gs'
ELSE COALESCE(inv.name, inv.ref, '')
END AS "Rechnungsnummer",
COALESCE(rc_inv.name->>'en_US', rc_inv.name->>'de_DE', '') AS "Rechnungsland",
COALESCE(rc_inv.code, '') AS "Rechnungsland Ländercode",
COALESCE(apt.name->>'en_US', apt.name->>'de_DE', '') AS "Zahlart",
COALESCE(itr.provider_reference, '') AS "Zahlungsreference der Bestellung",
COALESCE(partner.vat, '') AS "USt. Id / VAT Id des Käufers (in Bestellung)",
ROUND(COALESCE(ts.brutto_normal, 0) + COALESCE(ts.brutto_erm, 0), 2) AS "Gesamtpreis brutto",
ROUND(COALESCE(ts.netto_normal, 0) + COALESCE(ts.netto_erm, 0), 2) AS "Gesamtpreis netto",
COALESCE(ts.brutto_normal, 0) AS "Brutto",
COALESCE(ts.brutto_erm, 0) AS "Brutto erm",
COALESCE(ts.ust_satz_normal, 0) AS "USt. Satz",
COALESCE(ts.ust_satz_erm, 0) AS "Ust. Satz erm",
COALESCE(ts.netto_normal, 0) AS "Netto Betrag",
COALESCE(ts.netto_erm, 0) AS "Netto Betrag erm",
COALESCE(sh.shipping_brutto, 0) AS "Versandkosten brutto",
COALESCE(sh.shipping_netto, 0) AS "Versandkosten netto",
COALESCE(TO_CHAR(si.order_date, 'YYYY-MM-DD'), '') AS "Auftragsdatum",
COALESCE(inv.invoice_origin, '') AS "Bestellnummer",
COALESCE(TO_CHAR(ip.payment_date, 'YYYY-MM-DD'), '') AS "Zahldatum",
COALESCE(ship.name, partner.name, '') AS "Lieferadresse Name",
COALESCE(it.tags, '') AS "Tag",
COALESCE(
rc_ship.name->>'en_US', rc_ship.name->>'de_DE',
rc_inv.name->>'en_US', rc_inv.name->>'de_DE',
''
) AS "Lieferland"
FROM invoices inv
JOIN res_partner partner
ON partner.id = inv.partner_id
LEFT JOIN res_country rc_inv
ON rc_inv.id = partner.country_id
LEFT JOIN res_partner ship
ON ship.id = inv.partner_shipping_id
LEFT JOIN res_country rc_ship
ON rc_ship.id = ship.country_id
LEFT JOIN account_payment_term apt
ON apt.id = inv.invoice_payment_term_id
LEFT JOIN tax_split ts
ON ts.invoice_id = inv.id
LEFT JOIN shipping sh
ON sh.invoice_id = inv.id
LEFT JOIN invoice_tags it
ON it.invoice_id = inv.id
LEFT JOIN invoice_transaction_reference itr
ON itr.invoice_id = inv.id
AND itr.rn = 1
LEFT JOIN sale_info si
ON si.invoice_id = inv.id
LEFT JOIN invoice_payments ip
ON ip.invoice_id = inv.id
),
-- ===========================================================================
-- AMAZON-Seite
-- ===========================================================================
-- Basiszahlen + effektiver USt-Satz je Transaktionszeile.
amazon_base AS (
SELECT
a.transaction_complete_date,
a.transaction_depart_date,
a.vat_inv_number,
a.sale_arrival_country,
a.arrival_country,
a.transaction_event_id,
a.buyer_vat_number,
a.buyer_name,
a.activity_transaction_id,
COALESCE(a.total_activity_value_amt_vat_incl, 0) AS gross,
COALESCE(a.total_activity_value_amt_vat_excl, 0) AS net,
COALESCE(a.total_ship_charge_amt_vat_incl, 0) AS ship_gross,
COALESCE(a.total_ship_charge_amt_vat_excl, 0) AS ship_net,
CASE
WHEN COALESCE(a.total_activity_value_amt_vat_excl, 0) <> 0
THEN ROUND((COALESCE(a.total_activity_value_vat_amt, 0)
/ a.total_activity_value_amt_vat_excl * 100)::numeric, 2)
ELSE 0
END AS rate
FROM brandize_datacenter_amazon_vat_transaction a
WHERE a.activity_period = %(month)s
-- Optional: Gebuehrenzeilen ausschliessen (wie in VAT_Summary.sql):
-- AND a.sales_channel <> 'AMAZON_FEE'
),
amazon_rows AS (
SELECT
TO_CHAR(COALESCE(b.transaction_complete_date, b.transaction_depart_date), 'YYYY-MM-DD')
AS "Rechnungsdatum",
COALESCE(b.vat_inv_number, '') AS "Rechnungsnummer",
COALESCE(rc_inv.name->>'en_US', rc_inv.name->>'de_DE', '') AS "Rechnungsland",
COALESCE(b.sale_arrival_country, '') AS "Rechnungsland Ländercode",
'' AS "Zahlart",
COALESCE(b.transaction_event_id, '') AS "Zahlungsreference der Bestellung",
COALESCE(b.buyer_vat_number, '') AS "USt. Id / VAT Id des Käufers (in Bestellung)",
ROUND(b.gross::numeric, 2) AS "Gesamtpreis brutto",
ROUND(b.net::numeric, 2) AS "Gesamtpreis netto",
CASE
WHEN (b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
THEN ROUND(b.gross::numeric, 2) ELSE 0
END AS "Brutto",
CASE
WHEN NOT (
(b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
)
THEN ROUND(b.gross::numeric, 2) ELSE 0
END AS "Brutto erm",
CASE
WHEN (b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
THEN b.rate ELSE 0
END AS "USt. Satz",
CASE
WHEN NOT (
(b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
)
THEN b.rate ELSE 0
END AS "Ust. Satz erm",
CASE
WHEN (b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
THEN ROUND(b.net::numeric, 2) ELSE 0
END AS "Netto Betrag",
CASE
WHEN NOT (
(b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
)
THEN ROUND(b.net::numeric, 2) ELSE 0
END AS "Netto Betrag erm",
ROUND(b.ship_gross::numeric, 2) AS "Versandkosten brutto",
ROUND(b.ship_net::numeric, 2) AS "Versandkosten netto",
COALESCE(TO_CHAR(b.transaction_depart_date, 'YYYY-MM-DD'), '') AS "Auftragsdatum",
COALESCE(b.activity_transaction_id, '') AS "Bestellnummer",
'' AS "Zahldatum",
COALESCE(b.buyer_name, '') AS "Lieferadresse Name",
'' AS "Tag",
COALESCE(rc_ship.name->>'en_US', rc_ship.name->>'de_DE', '') AS "Lieferland"
FROM amazon_base b
LEFT JOIN res_country rc_inv
ON rc_inv.code = b.sale_arrival_country
LEFT JOIN res_country rc_ship
ON rc_ship.code = b.arrival_country
)
-- ===========================================================================
-- Zusammenfuehrung: ODOO + AMAZON, je Zeile mit Quelle
-- ===========================================================================
SELECT *, 'ODOO' AS "Quelle" FROM odoo_rows
UNION ALL
SELECT *, 'AMAZON' AS "Quelle" FROM amazon_rows
ORDER BY "Rechnungsdatum", "Rechnungsnummer";
+428
View File
@@ -0,0 +1,428 @@
-- =============================================================================
-- Quartal Schweiz – Gesamt (ODOO + AMAZON), Lieferung in die Schweiz
-- Odoo 17 / PostgreSQL
--
-- Zweck:
-- Dieselben Spalten wie Alle_Rechnungen.sql (inkl. "Quelle"), aber:
-- * Zeitraum = QUARTAL statt Monat
-- * nur Rechnungen mit Lieferung in die SCHWEIZ (Lieferland = 'CH')
--
-- Modelle:
-- ODOO : account.move (Kundenrechnungen / Gutschriften)
-- AMAZON : brandize_datacenter_amazon_vat_transaction
--
-- Parameter:
-- %(quarter)s Quartal als Text, z. B. '2026-Q3'
-- ODOO : Rechnungsdatum im Quartal (siehe quarters-CTE)
-- AMAZON: activity_period in den 3 Monaten des Quartals
--
-- Aufruf (Odoo / psycopg2):
-- cr.execute(open('Quartal_Schweiz.sql').read(), {'quarter': '2026-Q3'})
--
-- In psql stattdessen:
-- \set quarter '2026-Q3'
--
-- ---------------------------------------------------------------------------
-- Spalten-Mapping ODOO -> AMAZON
-- (Felder ohne Amazon-Pendant bleiben leer)
-- ---------------------------------------------------------------------------
-- Rechnungsdatum <- transaction_complete_date (Fallback: depart)
-- Rechnungsnummer <- vat_inv_number
-- Rechnungsland <- Name zu sale_arrival_country (res_country)
-- Rechnungsland Code <- sale_arrival_country
-- Zahlart <- '' (kein Amazon-Feld)
-- Zahlungsreference <- transaction_event_id
-- USt. Id Kaeufer <- buyer_vat_number
-- Gesamtpreis brutto <- total_activity_value_amt_vat_incl
-- Gesamtpreis netto <- total_activity_value_amt_vat_excl
-- Brutto / -erm <- Steuersplit nach effektivem Satz (Schwelle
-- USt. Satz / -erm CH >= 8, sonst >= 16 - wie im ODOO-Export)
-- Netto Betrag / -erm Satz = total_activity_value_vat_amt / netto * 100
-- Versandkosten brutto <- total_ship_charge_amt_vat_incl
-- Versandkosten netto <- total_ship_charge_amt_vat_excl
-- Auftragsdatum <- transaction_depart_date
-- Bestellnummer <- activity_transaction_id
-- Zahldatum <- '' (kein Amazon-Feld)
-- Lieferadresse Name <- buyer_name
-- Tag <- '' (kein Amazon-Feld)
-- Lieferland <- Name zu arrival_country (res_country)
--
-- ACHTUNG: Das Amazon-Mapping ist eine 1:1-Uebertragung auf die ODOO-Spalten.
-- Die Datumszuordnung (Rechnungsdatum/Auftragsdatum) sowie Bestellnummer
-- sollten gegen die tatsaechliche Amazon-Report-Semantik geprueft und hier
-- ggf. angepasst werden.
-- =============================================================================
WITH params AS (
-- Quartal aus %(quarter)s (z. B. '2026-Q3') ableiten.
SELECT
split_part(%(quarter)s, '-', 1) AS yr,
right(split_part(%(quarter)s, '-', 2), 1)::int AS qn
),
quarters AS (
SELECT
yr,
qn,
make_date(yr::int, (qn - 1) * 3 + 1, 1) AS first_day,
make_date(yr::int, (qn - 1) * 3 + 1, 1) + INTERVAL '3 months' AS next_day
FROM params
),
-- Die drei activity_period-Werte (Format 'YYYY-MMM', deutsche Kuerzel) fuer die
-- Amazon-Tabelle.
qmonths AS (
SELECT
q.yr || '-' || CASE ((q.qn - 1) * 3 + m.m)
WHEN 1 THEN 'JAN' WHEN 2 THEN 'FEB' WHEN 3 THEN 'MÄR'
WHEN 4 THEN 'APR' WHEN 5 THEN 'MAI' WHEN 6 THEN 'JUN'
WHEN 7 THEN 'JUL' WHEN 8 THEN 'AUG' WHEN 9 THEN 'SEP'
WHEN 10 THEN 'OKT' WHEN 11 THEN 'NOV' WHEN 12 THEN 'DEZ'
END AS activity_period
FROM quarters q
CROSS JOIN generate_series(1, 3) AS m(m)
),
-- ===========================================================================
-- ODOO-Seite (identisch zu Rechnungsexport.sql)
-- ===========================================================================
invoices AS (
SELECT
am.id,
am.name,
am.ref,
am.move_type,
am.invoice_date,
am.invoice_origin,
am.partner_id,
am.partner_shipping_id,
am.invoice_payment_term_id,
am.payment_reference
FROM account_move am
CROSS JOIN quarters q
WHERE am.invoice_date >= q.first_day
AND am.invoice_date < q.next_day
AND am.move_type IN ('out_invoice', 'out_refund')
),
line_taxes AS (
SELECT
aml.move_id AS invoice_id,
at.amount AS tax_rate,
SUM(aml.price_subtotal) AS netto,
SUM(aml.price_total) AS brutto
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN account_move_line_account_tax_rel amlatr
ON amlatr.account_move_line_id = aml.id
JOIN account_tax at
ON at.id = amlatr.account_tax_id
GROUP BY aml.move_id, at.amount
),
tax_split AS (
SELECT
lt.invoice_id,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.brutto::numeric, 2)
END) AS brutto_normal,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.tax_rate::numeric, 2)
END) AS ust_satz_normal,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.netto::numeric, 2)
END) AS netto_normal,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.brutto::numeric, 2)
END) AS brutto_erm,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.tax_rate::numeric, 2)
END) AS ust_satz_erm,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.netto::numeric, 2)
END) AS netto_erm
FROM line_taxes lt
JOIN invoices inv
ON inv.id = lt.invoice_id
JOIN res_partner rp
ON rp.id = inv.partner_id
LEFT JOIN res_country rc
ON rc.id = rp.country_id
GROUP BY lt.invoice_id
),
shipping AS (
SELECT
aml.move_id AS invoice_id,
ROUND(SUM(aml.price_total)::numeric, 2) AS shipping_brutto,
ROUND(SUM(aml.price_subtotal)::numeric, 2) AS shipping_netto
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN product_product pp
ON pp.id = aml.product_id
JOIN product_template pt
ON pt.id = pp.product_tmpl_id
WHERE pt.categ_id = 9
GROUP BY aml.move_id
),
invoice_tags AS (
SELECT
inv.id AS invoice_id,
STRING_AGG(
DISTINCT COALESCE(ct.name->>'en_US', ct.name->>'de_DE', ''),
', ' ORDER BY COALESCE(ct.name->>'en_US', ct.name->>'de_DE', '')
) AS tags
FROM invoices inv
JOIN sale_order so
ON so.name = inv.invoice_origin
JOIN sale_order_tag_rel sotr
ON sotr.order_id = so.id
JOIN crm_tag ct
ON ct.id = sotr.tag_id
GROUP BY inv.id
),
invoice_transaction_reference AS (
SELECT
rel.invoice_id,
pt.provider_reference,
ROW_NUMBER() OVER (
PARTITION BY rel.invoice_id
ORDER BY pt.id
) AS rn
FROM account_invoice_transaction_rel rel
JOIN payment_transaction pt
ON pt.id = rel.transaction_id
),
sale_info AS (
SELECT
inv.id AS invoice_id,
MIN(so.date_order) AS order_date
FROM invoices inv
JOIN sale_order so
ON so.name = inv.invoice_origin
GROUP BY inv.id
),
invoice_payments AS (
SELECT
aml.move_id AS invoice_id,
MAX(apr.max_date) AS payment_date
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN account_account aa
ON aa.id = aml.account_id
AND aa.account_type = 'asset_receivable'
JOIN account_partial_reconcile apr
ON apr.debit_move_id = aml.id
OR apr.credit_move_id = aml.id
GROUP BY aml.move_id
),
odoo_rows AS (
SELECT
TO_CHAR(inv.invoice_date, 'YYYY-MM-DD') AS "Rechnungsdatum",
CASE
WHEN inv.move_type = 'out_refund'
THEN COALESCE(inv.name, inv.ref, '') || '-gs'
ELSE COALESCE(inv.name, inv.ref, '')
END AS "Rechnungsnummer",
COALESCE(rc_inv.name->>'en_US', rc_inv.name->>'de_DE', '') AS "Rechnungsland",
COALESCE(rc_inv.code, '') AS "Rechnungsland Ländercode",
COALESCE(apt.name->>'en_US', apt.name->>'de_DE', '') AS "Zahlart",
COALESCE(itr.provider_reference, '') AS "Zahlungsreference der Bestellung",
COALESCE(partner.vat, '') AS "USt. Id / VAT Id des Käufers (in Bestellung)",
ROUND(COALESCE(ts.brutto_normal, 0) + COALESCE(ts.brutto_erm, 0), 2) AS "Gesamtpreis brutto",
ROUND(COALESCE(ts.netto_normal, 0) + COALESCE(ts.netto_erm, 0), 2) AS "Gesamtpreis netto",
COALESCE(ts.brutto_normal, 0) AS "Brutto",
COALESCE(ts.brutto_erm, 0) AS "Brutto erm",
COALESCE(ts.ust_satz_normal, 0) AS "USt. Satz",
COALESCE(ts.ust_satz_erm, 0) AS "Ust. Satz erm",
COALESCE(ts.netto_normal, 0) AS "Netto Betrag",
COALESCE(ts.netto_erm, 0) AS "Netto Betrag erm",
COALESCE(sh.shipping_brutto, 0) AS "Versandkosten brutto",
COALESCE(sh.shipping_netto, 0) AS "Versandkosten netto",
COALESCE(TO_CHAR(si.order_date, 'YYYY-MM-DD'), '') AS "Auftragsdatum",
COALESCE(inv.invoice_origin, '') AS "Bestellnummer",
COALESCE(TO_CHAR(ip.payment_date, 'YYYY-MM-DD'), '') AS "Zahldatum",
COALESCE(ship.name, partner.name, '') AS "Lieferadresse Name",
COALESCE(it.tags, '') AS "Tag",
COALESCE(
rc_ship.name->>'en_US', rc_ship.name->>'de_DE',
rc_inv.name->>'en_US', rc_inv.name->>'de_DE',
''
) AS "Lieferland"
FROM invoices inv
JOIN res_partner partner
ON partner.id = inv.partner_id
LEFT JOIN res_country rc_inv
ON rc_inv.id = partner.country_id
LEFT JOIN res_partner ship
ON ship.id = inv.partner_shipping_id
LEFT JOIN res_country rc_ship
ON rc_ship.id = ship.country_id
LEFT JOIN account_payment_term apt
ON apt.id = inv.invoice_payment_term_id
LEFT JOIN tax_split ts
ON ts.invoice_id = inv.id
LEFT JOIN shipping sh
ON sh.invoice_id = inv.id
LEFT JOIN invoice_tags it
ON it.invoice_id = inv.id
LEFT JOIN invoice_transaction_reference itr
ON itr.invoice_id = inv.id
AND itr.rn = 1
LEFT JOIN sale_info si
ON si.invoice_id = inv.id
LEFT JOIN invoice_payments ip
ON ip.invoice_id = inv.id
-- Nur Lieferungen in die Schweiz (Versandland, Fallback: Rechnungsland).
WHERE COALESCE(rc_ship.code, rc_inv.code) = 'CH'
),
-- ===========================================================================
-- AMAZON-Seite
-- ===========================================================================
-- Basiszahlen + effektiver USt-Satz je Transaktionszeile.
amazon_base AS (
SELECT
a.transaction_complete_date,
a.transaction_depart_date,
a.vat_inv_number,
a.sale_arrival_country,
a.arrival_country,
a.transaction_event_id,
a.buyer_vat_number,
a.buyer_name,
a.activity_transaction_id,
COALESCE(a.total_activity_value_amt_vat_incl, 0) AS gross,
COALESCE(a.total_activity_value_amt_vat_excl, 0) AS net,
COALESCE(a.total_ship_charge_amt_vat_incl, 0) AS ship_gross,
COALESCE(a.total_ship_charge_amt_vat_excl, 0) AS ship_net,
CASE
WHEN COALESCE(a.total_activity_value_amt_vat_excl, 0) <> 0
THEN ROUND((COALESCE(a.total_activity_value_vat_amt, 0)
/ a.total_activity_value_amt_vat_excl * 100)::numeric, 2)
ELSE 0
END AS rate
FROM brandize_datacenter_amazon_vat_transaction a
WHERE a.activity_period IN (SELECT activity_period FROM qmonths)
AND a.arrival_country = 'CH'
-- Optional: Gebuehrenzeilen ausschliessen (wie in VAT_Summary.sql):
-- AND a.sales_channel <> 'AMAZON_FEE'
),
amazon_rows AS (
SELECT
TO_CHAR(COALESCE(b.transaction_complete_date, b.transaction_depart_date), 'YYYY-MM-DD')
AS "Rechnungsdatum",
COALESCE(b.vat_inv_number, '') AS "Rechnungsnummer",
COALESCE(rc_inv.name->>'en_US', rc_inv.name->>'de_DE', '') AS "Rechnungsland",
COALESCE(b.sale_arrival_country, '') AS "Rechnungsland Ländercode",
'' AS "Zahlart",
COALESCE(b.transaction_event_id, '') AS "Zahlungsreference der Bestellung",
COALESCE(b.buyer_vat_number, '') AS "USt. Id / VAT Id des Käufers (in Bestellung)",
ROUND(b.gross::numeric, 2) AS "Gesamtpreis brutto",
ROUND(b.net::numeric, 2) AS "Gesamtpreis netto",
CASE
WHEN (b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
THEN ROUND(b.gross::numeric, 2) ELSE 0
END AS "Brutto",
CASE
WHEN NOT (
(b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
)
THEN ROUND(b.gross::numeric, 2) ELSE 0
END AS "Brutto erm",
CASE
WHEN (b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
THEN b.rate ELSE 0
END AS "USt. Satz",
CASE
WHEN NOT (
(b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
)
THEN b.rate ELSE 0
END AS "Ust. Satz erm",
CASE
WHEN (b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
THEN ROUND(b.net::numeric, 2) ELSE 0
END AS "Netto Betrag",
CASE
WHEN NOT (
(b.sale_arrival_country = 'CH' AND b.rate >= 8)
OR (COALESCE(b.sale_arrival_country, '') <> 'CH' AND b.rate >= 16)
)
THEN ROUND(b.net::numeric, 2) ELSE 0
END AS "Netto Betrag erm",
ROUND(b.ship_gross::numeric, 2) AS "Versandkosten brutto",
ROUND(b.ship_net::numeric, 2) AS "Versandkosten netto",
COALESCE(TO_CHAR(b.transaction_depart_date, 'YYYY-MM-DD'), '') AS "Auftragsdatum",
COALESCE(b.activity_transaction_id, '') AS "Bestellnummer",
'' AS "Zahldatum",
COALESCE(b.buyer_name, '') AS "Lieferadresse Name",
'' AS "Tag",
COALESCE(rc_ship.name->>'en_US', rc_ship.name->>'de_DE', '') AS "Lieferland"
FROM amazon_base b
LEFT JOIN res_country rc_inv
ON rc_inv.code = b.sale_arrival_country
LEFT JOIN res_country rc_ship
ON rc_ship.code = b.arrival_country
)
-- ===========================================================================
-- Zusammenfuehrung: ODOO + AMAZON, je Zeile mit Quelle
-- ===========================================================================
SELECT *, 'ODOO' AS "Quelle" FROM odoo_rows
UNION ALL
SELECT *, 'AMAZON' AS "Quelle" FROM amazon_rows
ORDER BY "Rechnungsdatum", "Rechnungsnummer";
+292
View File
@@ -0,0 +1,292 @@
-- =============================================================================
-- Rechnungsexport – Odoo 17 / PostgreSQL
-- Rechnungsdatum · Rechnungsnummer · Länder · Zahlart · USt-Nr. · Steuersplit
--
-- Modell : account.move (Kundenrechnungen und Gutschriften)
--
-- Parameter:
-- %(month)s Abrechnungszeitraum als Text, z. B. '2026-SEP'
--
-- Aufruf (Odoo / psycopg2):
-- cr.execute(open('Rechnungsexport.sql').read(), {'month': '2026-SEP'})
--
-- In psql stattdessen:
-- \set month '2026-SEP'
-- ... AND am.invoice_date >= :'month' (siehe params-CTE)
--
-- Hinweis zum Monatsfilter:
-- Das Rechnungsdatum wird auf einen vollen Kalendermonat gefiltert.
-- Der Monat wird locale-unabhängig in ein Datum umgerechnet; es werden
-- deutsche (MÄR, MAI, OKT, DEZ) und englische (MAR, MAY, OCT, DEC)
-- Dreibuchstaben-Kürzel akzeptiert.
--
-- Änderungen gegenüber der Vorversion (nur die fehlenden Felder ergänzt):
-- * Auftragsdatum : aus sale_order.date_order (über invoice_origin)
-- * Zahldatum : aus der letzten Ausgleichsbuchung der Debitorenzeile
-- * Lieferadresse Name / Lieferland : aus account_move.partner_shipping_id
-- (Fallback: Rechnungspartner)
-- =============================================================================
WITH params AS (
-- Kalendermonat aus %(month)s (z. B. '2026-SEP') ableiten.
SELECT
to_date(
split_part(%(month)s, '-', 1) || '-' ||
CASE upper(split_part(%(month)s, '-', 2))
WHEN 'JAN' THEN '01' WHEN 'FEB' THEN '02'
WHEN 'MÄR' THEN '03' WHEN 'MAR' THEN '03' WHEN 'APR' THEN '04'
WHEN 'MAI' THEN '05' WHEN 'MAY' THEN '05' WHEN 'JUN' THEN '06'
WHEN 'JUL' THEN '07' WHEN 'AUG' THEN '08' WHEN 'SEP' THEN '09'
WHEN 'OKT' THEN '10' WHEN 'OCT' THEN '10' WHEN 'NOV' THEN '11'
WHEN 'DEZ' THEN '12' WHEN 'DEC' THEN '12'
END,
'YYYY-MM'
) AS first_day
),
invoices AS (
SELECT
am.id,
am.name,
am.ref,
am.move_type,
am.invoice_date,
am.invoice_origin,
am.partner_id,
am.partner_shipping_id,
am.invoice_payment_term_id,
am.payment_reference
FROM account_move am
CROSS JOIN params p
WHERE am.invoice_date >= p.first_day
AND am.invoice_date < p.first_day + INTERVAL '1 month'
AND am.move_type IN ('out_invoice', 'out_refund')
),
-- ---------------------------------------------------------------------------
-- tax summary: invoice line -> tax relation -> account_tax
-- ---------------------------------------------------------------------------
line_taxes AS (
SELECT
aml.move_id AS invoice_id,
at.amount AS tax_rate,
SUM(aml.price_subtotal) AS netto,
SUM(aml.price_total) AS brutto
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN account_move_line_account_tax_rel amlatr
ON amlatr.account_move_line_id = aml.id
JOIN account_tax at
ON at.id = amlatr.account_tax_id
GROUP BY aml.move_id, at.amount
),
-- ---------------------------------------------------------------------------
-- tax split logic from Python:
-- threshold = 8 for CH, else 16
-- ---------------------------------------------------------------------------
tax_split AS (
SELECT
lt.invoice_id,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.brutto::numeric, 2)
END) AS brutto_normal,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.tax_rate::numeric, 2)
END) AS ust_satz_normal,
MAX(CASE
WHEN (rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
THEN ROUND(lt.netto::numeric, 2)
END) AS netto_normal,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.brutto::numeric, 2)
END) AS brutto_erm,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.tax_rate::numeric, 2)
END) AS ust_satz_erm,
MAX(CASE
WHEN NOT (
(rc.code = 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 8)
OR (COALESCE(rc.code, '') <> 'CH' AND ROUND(lt.tax_rate::numeric, 2) >= 16)
)
THEN ROUND(lt.netto::numeric, 2)
END) AS netto_erm
FROM line_taxes lt
JOIN invoices inv
ON inv.id = lt.invoice_id
JOIN res_partner rp
ON rp.id = inv.partner_id
LEFT JOIN res_country rc
ON rc.id = rp.country_id
GROUP BY lt.invoice_id
),
-- ---------------------------------------------------------------------------
-- shipping costs (product category id = 9, as in Python logic)
-- ---------------------------------------------------------------------------
shipping AS (
SELECT
aml.move_id AS invoice_id,
ROUND(SUM(aml.price_total)::numeric, 2) AS shipping_brutto,
ROUND(SUM(aml.price_subtotal)::numeric, 2) AS shipping_netto
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN product_product pp
ON pp.id = aml.product_id
JOIN product_template pt
ON pt.id = pp.product_tmpl_id
WHERE pt.categ_id = 9
GROUP BY aml.move_id
),
invoice_tags AS (
SELECT
inv.id AS invoice_id,
STRING_AGG(
DISTINCT COALESCE(ct.name->>'en_US', ct.name->>'de_DE', ''),
', ' ORDER BY COALESCE(ct.name->>'en_US', ct.name->>'de_DE', '')
) AS tags
FROM invoices inv
JOIN sale_order so
ON so.name = inv.invoice_origin
JOIN sale_order_tag_rel sotr
ON sotr.order_id = so.id
JOIN crm_tag ct
ON ct.id = sotr.tag_id
GROUP BY inv.id
),
invoice_transaction_reference AS (
SELECT
rel.invoice_id,
pt.provider_reference,
ROW_NUMBER() OVER (
PARTITION BY rel.invoice_id
ORDER BY pt.id
) AS rn
FROM account_invoice_transaction_rel rel
JOIN payment_transaction pt
ON pt.id = rel.transaction_id
),
-- ---------------------------------------------------------------------------
-- Auftragsdatum: Bestellung über invoice_origin (Verkaufsauftragsname) finden
-- ---------------------------------------------------------------------------
sale_info AS (
SELECT
inv.id AS invoice_id,
MIN(so.date_order) AS order_date
FROM invoices inv
JOIN sale_order so
ON so.name = inv.invoice_origin
GROUP BY inv.id
),
-- ---------------------------------------------------------------------------
-- Zahldatum: Datum der letzten Ausgleichsbuchung der Debitorenzeile.
-- account_partial_reconcile.max_date = jüngeres Datum der beiden Buchungen.
-- ---------------------------------------------------------------------------
invoice_payments AS (
SELECT
aml.move_id AS invoice_id,
MAX(apr.max_date) AS payment_date
FROM invoices inv
JOIN account_move_line aml
ON aml.move_id = inv.id
JOIN account_account aa
ON aa.id = aml.account_id
AND aa.account_type = 'asset_receivable'
JOIN account_partial_reconcile apr
ON apr.debit_move_id = aml.id
OR apr.credit_move_id = aml.id
GROUP BY aml.move_id
)
SELECT
TO_CHAR(inv.invoice_date, 'YYYY-MM-DD') AS "Rechnungsdatum",
CASE
WHEN inv.move_type = 'out_refund'
THEN COALESCE(inv.name, inv.ref, '') || '-gs'
ELSE COALESCE(inv.name, inv.ref, '')
END AS "Rechnungsnummer",
COALESCE(rc_inv.name->>'en_US', rc_inv.name->>'de_DE', '') AS "Rechnungsland",
COALESCE(rc_inv.code, '') AS "Rechnungsland Ländercode",
COALESCE(apt.name->>'en_US', apt.name->>'de_DE', '') AS "Zahlart",
COALESCE(itr.provider_reference, '') AS "Zahlungsreference der Bestellung",
COALESCE(partner.vat, '') AS "USt. Id / VAT Id des Käufers (in Bestellung)",
ROUND(COALESCE(ts.brutto_normal, 0) + COALESCE(ts.brutto_erm, 0), 2) AS "Gesamtpreis brutto",
ROUND(COALESCE(ts.netto_normal, 0) + COALESCE(ts.netto_erm, 0), 2) AS "Gesamtpreis netto",
COALESCE(ts.brutto_normal, 0) AS "Brutto",
COALESCE(ts.brutto_erm, 0) AS "Brutto erm",
COALESCE(ts.ust_satz_normal, 0) AS "USt. Satz",
COALESCE(ts.ust_satz_erm, 0) AS "Ust. Satz erm",
COALESCE(ts.netto_normal, 0) AS "Netto Betrag",
COALESCE(ts.netto_erm, 0) AS "Netto Betrag erm",
COALESCE(sh.shipping_brutto, 0) AS "Versandkosten brutto",
COALESCE(sh.shipping_netto, 0) AS "Versandkosten netto",
COALESCE(TO_CHAR(si.order_date, 'YYYY-MM-DD'), '') AS "Auftragsdatum",
COALESCE(inv.invoice_origin, '') AS "Bestellnummer",
COALESCE(TO_CHAR(ip.payment_date, 'YYYY-MM-DD'), '') AS "Zahldatum",
COALESCE(ship.name, partner.name, '') AS "Lieferadresse Name",
COALESCE(it.tags, '') AS "Tag",
COALESCE(
rc_ship.name->>'en_US', rc_ship.name->>'de_DE',
rc_inv.name->>'en_US', rc_inv.name->>'de_DE',
''
) AS "Lieferland"
FROM invoices inv
JOIN res_partner partner
ON partner.id = inv.partner_id
LEFT JOIN res_country rc_inv
ON rc_inv.id = partner.country_id
LEFT JOIN res_partner ship
ON ship.id = inv.partner_shipping_id
LEFT JOIN res_country rc_ship
ON rc_ship.id = ship.country_id
LEFT JOIN account_payment_term apt
ON apt.id = inv.invoice_payment_term_id
LEFT JOIN tax_split ts
ON ts.invoice_id = inv.id
LEFT JOIN shipping sh
ON sh.invoice_id = inv.id
LEFT JOIN invoice_tags it
ON it.invoice_id = inv.id
LEFT JOIN invoice_transaction_reference itr
ON itr.invoice_id = inv.id
AND itr.rn = 1
LEFT JOIN sale_info si
ON si.invoice_id = inv.id
LEFT JOIN invoice_payments ip
ON ip.invoice_id = inv.id
ORDER BY inv.invoice_date, inv.name;