Przy cenach z podatkiem i wyłączonym handlu transgranicznym Magento zakłada, że cena zawiera VAT według stawki sklepu, a nalicza VAT według stawki klienta. Stawka sklepu bierze się z adresu nadania w ustawieniach wysyłki. Jeśli dla tego adresu nie ma pasującej stawki, sklep traktuje cenę brutto jak netto i dolicza VAT klienta.
| Konfiguracja | Magento Open Source 2.4.8, sprzedaż wyłącznie w Polsce, ceny w katalogu brutto, klienci firmowi widzą ceny netto |
|---|---|
| Objaw | po włączeniu „ceny zawierają podatek” VAT dalej doliczany na wierzchu |
| Przyczyna | adres nadania z wartościami fabrycznymi (USA), dla którego nie było stawki: stawka sklepu 0%, stawka klienta 23% |
| Zmiany | adres nadania, domyślny adres podatkowy i handel transgraniczny zmienione razem, stawka i reguła dla Polski |
| Wynik | stawka sklepu i klienta 23%; karta i koszyk zgodne co do grosza; koszyk z rabatem konta liczy VAT po rabacie |
Objaw: VAT na wierzchu mimo „ceny zawierają podatek”
Punkt wyjścia w opisanym sklepie: ceny przychodziły do katalogu jako brutto, a konfiguracja podatków była fabryczna. Jedynymi stawkami były przykładowe US-CA 8,25% i US-NY 8,375%, które Magento zakłada przy instalacji.
Po dodaniu polskiej stawki 23% i włączeniu opcji „ceny w katalogu zawierają podatek” koszyk doliczał VAT do ceny, która już go zawierała. Przykład umowny: produkt za 123,00 zł brutto kosztował w koszyku 151,29 zł, czyli 123,00 zł plus 23%.
Przyczyna: stawka sklepu liczona z adresu nadania
Gdy ceny w katalogu zawierają podatek, Magento musi wiedzieć, jaki VAT w nich siedzi. W standardowym kodzie 2.4.8 odpowiada za to metoda getStoreRate() z klasy Magento\Tax\Model\Calculation. Liczy ona stawkę dla adresu nadania z ustawień wysyłki, czyli kraju, regionu i kodu pocztowego w „Shipping Settings → Origin”. Fabrycznie są to USA, region Kalifornia i kod 90034 (konfiguracja modułu Shipping).
Trzy ustawienia łatwo pomylić, bo wszystkie dotyczą adresów i podatków:
| Ustawienie | Rola przy podatku |
|---|---|
| adres nadania (Shipping Settings → Origin) | z niego liczy się stawka sklepu, czyli VAT zawarty w cenie katalogowej |
| domyślny adres podatkowy (Tax → Default Tax Destination Calculation) | adres zastępczy dla stawki klienta, gdy brakuje adresu klienta, np. u gościa |
| adres dostawy albo rozliczeniowy klienta | podstawa stawki klienta, zależnie od ustawienia „podatek liczony według” |
Przy wyłączonym handlu transgranicznym cena przelicza się ze stawki sklepu na stawkę klienta. W opisanym sklepie dla adresu nadania w USA nie było żadnej pasującej stawki, więc stawka sklepu wynosiła 0%: Magento uznawało 123,00 zł za kwotę netto i doliczało 23% klienta z Polski. Dokumentacja Adobe o ustawieniach wysyłki wskazuje, że adres nadania wpływa na stawkę podatku, i zaleca, żeby w sklepach z podatkami UE domyślny adres podatkowy odpowiadał adresowi nadania. To zalecenie zgodności, a nie jedno ustawienie.
Handel transgraniczny zmienia ten mechanizm. Gdy jest włączony, Magento nie przelicza ceny ze stawki sklepu na stawkę klienta: cena brutto zostaje stała, a zawarty w niej VAT liczy się stawką klienta (dokumentacja Adobe o ustawieniach podatków opisuje oba warianty). Dlatego przy diagnozie zawsze trzeba sprawdzić, który wariant jest włączony.
Diagnoza: stawka sklepu, stawka klienta i to, co je łączy
Sygnałem jest różnica między stawką sklepu (getStoreRate()) a stawką dla adresu klienta (getRate()). Sama różnica 0% i 23% nie wystarcza jednak do wskazania winnego ustawienia. Przed zmianą konfiguracji sprawdź po kolei:
- adres nadania w ustawieniach wysyłki: kraj, region i kod pocztowy
- czy handel transgraniczny jest włączony, bo od tego zależy, jak liczona jest stawka sklepu
- czy stawka i reguła podatkowa pasują do adresu nadania oraz do klasy podatkowej produktu i klasy klienta; stawka bez pasującej reguły daje 0% tak samo jak jej brak
- koszyk gościa: gość nie ma rabatów ani ceny własnej, więc jeśli on też dostaje VAT na wierzchu, przyczyna leży w podatkach, a nie w module rabatów
Wyłączenie ceny własnej pozycji koszyka nie jest dobrą kontrolą, jeśli moduł rabatów ustawia ją ponownie przy każdym przeliczeniu koszyka. Koszyk gościa jest pewniejszy.
Ustawienia w opisanym sklepie
Sklep sprzedaje wyłącznie w Polsce, więc poniższa konfiguracja jest właściwa dla tego przypadku, a nie uniwersalną instrukcją dla każdego sklepu. Zmieniono:
- adres nadania w ustawieniach wysyłki: Polska
- domyślny adres podatkowy: Polska, z regionem i kodem pocztowym pasującymi do definicji stawki (stawka z kodem
*pasuje do każdego kodu) - handel transgraniczny: włączony, czyli stała cena brutto niezależnie od stawki klienta; według dokumentacji Adobe wymaga to cen z podatkiem
- ceny w katalogu z podatkiem, podatek liczony po rabacie
- stawka 23% dla Polski i reguła łącząca klasę podatkową klienta z klasą produktu; sprawdzone, że każdy produkt ma klasę podatkową, a każda grupa klientów klasę klienta
- wyświetlanie: ceny netto w katalogu, koszyku i dokumentach, rozbicie VAT tylko w podsumowaniu
Adres nadania, domyślny adres podatkowy i handel transgraniczny zmieniono w jednym kroku, więc nie mierzyliśmy, która z tych zmian sama usunęła objaw. Z kodu wynika, że przy wyłączonym handlu transgranicznym wystarczyłaby zmiana adresu nadania, a przy włączonym cena brutto zostałaby stała nawet z adresem nadania w USA, bo Magento nie przelicza jej między stawkami. Stawka klienta, a u gościa stawka z domyślnego adresu podatkowego, decyduje wtedy tylko o tym, ile VAT siedzi w tej cenie. Sklep sprzedający do kilku krajów musi te decyzje podjąć osobno, razem ze stawkami dla tych krajów.
Osobny przypadek: cena netto liczona z adresu klienta
Drugi objaw miał inną przyczynę: na listingu cena netto była poprawna, a na karcie produktu u jednego klienta pod etykietą „Twoja cena netto” stała cena brutto minus rabat. Przykład umowny: przy cenie 123,00 zł brutto i rabacie 1% karta pokazywała 121,77 zł zamiast 99,00 zł.
Blok na karcie przeliczał cenę metodą Magento\Catalog\Helper\Data::getTaxPrice(), która bierze adres podatkowy z sesji klienta. To normalne działanie silnika podatkowego, a nie wada tej metody: Magento może liczyć podatek według adresu dostawy, rozliczeniowego albo nadania. Problem dotyczył konta, którego adres miał kraj spoza Unii bez żadnej stawki, więc VAT nie był zdejmowany. Listing był poprawny, bo liczył stawkę sklepu.
- decyzja dla opisanego sklepu: sprzedaż krajowa i jedna stawka, więc ceny w katalogu przeliczane są stawką sklepu, w jednym wspólnym miejscu; sklep sprzedający za granicę potrzebuje innej zasady
- diagnoza: gdy „u jednego klienta jest źle, u innych dobrze”, sprawdź kraje w adresach kont
- test w konsoli tego nie odtworzy, bo bez sesji klienta nie ma jego adresu; trzeba porównać stawki dla adresów konkretnych kont albo sprawdzić stronę w sesji klienta
Taki adres dało się zapisać, bo lista dozwolonych krajów obejmowała wszystkie kraje świata. W sklepie sprzedającym tylko w Polsce warto ją zawęzić.
Pułapka: wartości liczone z ceny brutto i netto naraz
Gdy sklep pokazuje cenę katalogową brutto obok ceny klienta netto, psuje się każda wartość pochodna: „oszczędność”, „rabat %”, różnica cen. Odjęcie netto od brutto zawyża rabat o cały VAT.
Przykład umowny: 10 sztuk po 123,00 zł brutto, rabat 1%. Poprawnie: 1 000,00 zł netto przed rabatem, 990,00 zł netto po rabacie, oszczędność 10,00 zł, czyli 1%. Po odjęciu kwoty netto od brutto wychodzi 240,00 zł, czyli około 19,5% „rabatu”. Przed każdą różnicą albo procentem obie kwoty trzeba sprowadzić do netto.
Zamówienia z VAT 0,00 złożone przed zmianą konfiguracji
Po wdrożeniu łatwo uznać, że zamówienia z podatkiem 0,00 dowodzą, że poprawka nie działa. Data pozwala to rozstrzygnąć: jeśli zamówienie powstało przed zapisaniem nowej konfiguracji podatków, nie jest dowodem dalszej awarii. W opisanym sklepie wszystkie takie zamówienia były starsze niż zapis ustawień.
Data nie potwierdza jednak, że kwoty w tych zamówieniach są poprawne. Zostały policzone według starej konfiguracji i to, jak je traktować w księgowości, jest osobną decyzją sklepu.
Testy odbioru podatków
W opisanym sklepie konfigurację sprawdziliśmy tak:
| Test | Wynik |
|---|---|
| stawka sklepu i stawka dla adresu w Polsce | obie 23% |
| karta produktu: cena netto i cena po rabacie konta | netto = brutto / 1,23; rabat od netto |
| koszyk z 2 szt.: netto, VAT i suma | suma = 2 × cena brutto, zgodna co do grosza |
| koszyk z rabatem konta 1% | suma = cena brutto × 0,99, VAT 23% liczony po rabacie |
| klasy podatkowe produktów i grup klientów | wszystkie produkty i wszystkie grupy objęte regułą |
Czego te testy nie objęły: zapisanego zamówienia. Sumy zamówienia Magento kopiuje z sum koszyka, ale ostatecznym potwierdzeniem jest kontrolowane zamówienie testowe złożone po zmianie i sprawdzenie zapisanych w nim kwot netto, VAT i sumy. Osobno trzeba sprawdzić moduły, które liczą ceny poza silnikiem podatkowym Magento, np. własny obieg przedsprzedaży.
Kiedy poprawisz to w panelu, a kiedy potrzebny jest przegląd cen
Sklep na standardowym wyświetlaniu cen, bez własnych bloków cen i bez rabatów per konto, sprzedający w jednym kraju. Adres nadania, domyślny adres podatkowy, decyzja o handlu transgranicznym, stawka i reguła oraz test koszyka gościa i zamówienia testowego zwykle wystarczą.
Sklep B2B z cenami netto dla firm, własnymi blokami cen, rabatami per konto, sprzedażą do kilku krajów albo osobnym obiegiem zamówień. Tam każde miejsce, które pokazuje cenę albo liczy z niej różnicę, trzeba przejrzeć osobno i sprawdzić na kontach z różnymi adresami.
Gdy zlecasz nam taki przegląd, dostajesz opis obecnej konfiguracji adresów i stawek, porównanie stawki sklepu i stawek kont, listę miejsc, które pokazują lub przeliczają cenę, poprawki tam, gdzie VAT liczy się inaczej niż przyjęta zasada albo ze stałej w kodzie, oraz raport z testów odbioru z zamówieniem testowym. Więcej o pracy przy sklepach na Magento i przy cenach i rabatach B2B.