429 lines
16 KiB
SQL
429 lines
16 KiB
SQL
-- =============================================================================
|
||
-- 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";
|