Wprowadzenie
Pole Monetary w Odoo często wygląda jak zwykła liczba, ale w praktyce rządzi nim logika walut i zaokrągleń — to dlatego tak często jest niewłaściwie używane. Gdy zrozumiesz jego mechanikę, przestaniesz stosować zwykły Float tam, gdzie liczy się pieniądz.
Jeżeli przeglądałeś kiedyś kwotę zamówienia sprzedaży, sumę faktury czy cenę produktu w Odoo, miałeś do czynienia z polem walutowym. Występuje ono praktycznie w każdym module i odpowiada za formatowanie, precyzję oraz reguły zaokrągleń, które dla finansów są krytyczne.
Ten materiał jest przeznaczony dla deweloperów Odoo, konsultantów i technicznych użytkowników biznesowych, którzy chcą zrozumieć, jak pole Monetary funkcjonuje w praktyce. Niezależnie czy uczysz się podstaw, tworzysz moduł czy diagnozujesz nieoczekiwane różnice w zaokrągleniach — znajdziesz tu niezbędne wskazówki.
Czym jest pole Walutowe w Odoo
Typ fields.Monetary to wbudowany rodzaj pola w Odoo zaprojektowany z myślą o wartościach wyrażonych w walucie: cenach, kwotach, sumach, budżetach — słowem: wszystkim, co oznacza pieniądze.
Kluczowa różnica względem zwykłego Float polega na powiązaniu z walutą. Pole Monetary zawsze wie, w jakiej walucie operuje i korzysta z tej informacji przy wyświetlaniu liczby i stosowaniu reguł zaokrągleń.
Jak wygląda w interfejsie
W UI Odoo wartości walutowe są prezentowane zgodnie z ustawieniami przypisanej waluty — symbol, separator tysięcy i liczba miejsc po przecinku odzwierciedlają konfigurację rekordu res.currency (np. € 1 234,50 lub $ 1,234.50).
Pole jest edytowalne w widoku formularza, czytelne w listach i działa poprawnie w tabelach przestawnych oraz raportach finansowych. Dla użytkownika końcowego formatowanie odbywa się automatycznie — Odoo zajmuje się resztą.
Co kryje się pod spodem
Na poziomie bazy danych wartość pola Monetary jest przechowywana jako typ zmiennoprzecinkowy (double precision) w PostgreSQL. Informacja o walucie nie jest trzymana w tej samej kolumnie — pochodzi z powiązanego rekordu res.currency, wskazywanego przez pole Many2one na tym samym modelu.
Rozdzielenie wartości i waluty jest celowe — pozwala zachować przejrzystość struktur danych i umożliwia zmianę waluty bez ingerencji w kolumnę z kwotą.
Jak pole działa
Zrozumienie tych zasad pozwoli unikać typowych problemów z zaokrągleniami i formatowaniem, które często pojawiają się przy niestandardowych implementacjach Odoo.
Parametr currency_field
Każde pole Monetary musi być powiązane z polem Many2one wskazującym na res.currency. Domyślnie Odoo spodziewa się pola o nazwie currency_id, ale można to nadpisać parametrem currency_field:
amount = fields.Monetary(string='Amount', currency_field='currency_id')
Jeżeli pole waluty nie istnieje dla rekordu, Odoo czasem użyje waluty firmy jako domyślnej. To zapobiega błędom, ale w środowisku wielowalutowym może prowadzić do mylącego formatowania. Zawsze deklaruj pole waluty jawnie.
Zaokrąglenia i precyzja
Monetary różni się od Float sposobem zaokrąglania: model res.currency definiuje liczbę miejsc po przecinku i czynnik zaokrąglenia. Gdy Odoo odczytuje lub wyświetla wartość walutową, stosuje te reguły automatycznie.
Przykładowo, wartość zapisana jako 1.2349999 dla waluty EUR wyświetli się jako 1.23 zgodnie z ustawieniem waluty, nie jako 1.235. To ma krytyczne znaczenie przy podatkach, sumach faktur i rozliczeniach — użycie zwykłego Float w takich miejscach prowadzi do trudnych do wykrycia rozbieżności.
Interakcja z ORM Odoo
W warstwie Pythona odczyt pola Monetary zwraca float. Kontekst waluty bierze się z powiązanego pola waluty rekordu. Przy obliczeniach używaj metody round() z obiektu waluty, aby zachować zgodność precyzji:
rounded_value = self.currency_id.round(self.amount)
Takie podejście minimalizuje błędy wynikające z kumulacji operacji na liczbach zmiennoprzecinkowych, które pojawiają się przy sumowaniu wielu linii czy wieloetapowych kalkulacjach.
Monetary w raportach QWeb
W szablonach QWeb pola Monetary korzystają ze specjalnego widgetu, który poprawnie formatuje wartości w PDF i na stronie:
<span t-esc="record.amount"
t-options='{"widget": "monetary", "display_currency": record.currency_id}'/>
Dzięki temu każdy wygenerowany dokument pokazuje właściwy symbol waluty i format liczb, zgodnie z walutą rekordu.
Zastosowania biznesowe
Gdzie w praktyce się przydaje — przykłady
1. Sprzedaż: ceny produktów i sumy zamówień
Pola takie jak price_unit, price_subtotal czy amount_total na zamówieniach sprzedaży są typu Monetary. Dzięki temu respektują walutę klienta, nawet gdy różni się od waluty firmy.
Kiedy handlowiec wystawia zamówienie w USD dla firmy działającej w EUR, Odoo poprawnie zajmie się prezentacją, zaokrągleniami i konwersjami, bo pole walutowe operuje w kontekście rekordu, nie systemowym.
2. Księgowość: kwoty faktur i linie podatkowe
W module księgowym wszystkie kolumny kwot na fakturze — amount_untaxed, amount_tax, amount_total — są polami Monetary. To właśnie waluta faktury determinuje zaokrąglenia tych wartości.
To nie jest drobiazg — nieprawidłowe zaokrąglenia na liniach podatkowych tworzą nierówności w zapisach księgowych i sprawiają problemy przy uzgadnianiu ksiąg. Pole Monetary eliminuje te problemy u źródła.
3. CRM: prognozowane przychody
Pole expected_revenue w CRM to pole Monetary. Pozwala rejestrować wartości w walucie prospektu, a raporty/analizy mogą konwertować je do waluty firmowej do porównań i prognoz.
Taki model działa sprawnie, bo pola walutowe niosą ze sobą informację o walucie razem z wartością liczbową.
4. Zakupy: ceny dostawców i zamówienia zakupowe
W zamówieniach zakupowych ceny jednostkowe i sumy są przechowywane jako Monetary i powiązane z walutą dostawcy. Faktura w jenach japońskich i faktura w euro obsługiwane są jednakowo — precyzja i prezentacja odbywają się automatycznie.
5. Pola niestandardowe: budżet i cele
Częstą modyfikacją jest dodanie pola budżetu, celu przychodów czy limitu kosztów do projektu, działu lub modelu niestandardowego. W takich przypadkach pole Monetary jest właściwym wyborem — integruje się z walutą firmy oraz poprawnie wyświetla się w formularzach, listach i raportach.
Technicznie można użyć Float, ale przy scenariuszach wielowalutowych skutkuje to niespójnościami formatowania i problemami z zaokrągleniami.
Tworzenie i dostosowanie pola
Jak dodać pole Monetary — dwie ścieżki
Są dwie podstawowe drogi: użyć Odoo Studio (bez kodu) lub stworzyć pole w module Python (pełna kontrola).
Odoo Studio
Studio oferuje typ Monetary w panelu dodawania pól. Jeśli model nie ma pola waluty, Studio automatycznie doda currency_id. Pola tworzone przez Studio mają przedrostek x_ (np. x_studio_budget). Jeśli na modelu istnieje już currency_id, nowe pole z niego skorzysta; w przeciwnym razie Studio doda własne pole waluty — warto to przejrzeć przed wdrożeniem, szczególnie gdy planujesz wiele pól walutowych o różnych walutach.
Dla prostych przypadków Studio jest najszybszym rozwiązaniem i sprawdzi się przy pracy użytkowników biznesowych bez dostępu programistycznego.
Podejście techniczne: pola w Pythonie
W module Python trzeba zadeklarować pole Monetary oraz odpowiadające mu pole Many2one do res.currency. To standardowy wzorzec w rozwoju Odoo:
from odoo import fields, models
class ProjectTask(models.Model):
_inherit = 'project.task'
x_budget = fields.Monetary(
string='Budget',
currency_field='x_budget_currency_id',
)
x_budget_currency_id = fields.Many2one(
comodel_name='res.currency',
string='Budget Currency',
default=lambda self: self.env.company.currency_id,
)
Ustawienie waluty firmy jako wartości domyślnej to praktyczne rozwiązanie dla pól wewnętrznych — zapobiega pustym polom waluty przy tworzeniu nowego rekordu i zapewnia poprawne formatowanie od razu.
Pola obliczane typu Monetary
Pola Monetary dobrze sprawdzają się jako pola obliczane. Gdy sumujesz pozycje lub stosujesz formułę zwracającą kwotę, deklarujesz pole jako compute i używasz standardowego wzorca:
x_total_budget = fields.Monetary(
string='Total Budget',
currency_field='currency_id',
compute='_compute_total_budget',
store=True,
)
@api.depends('x_line_ids.x_amount')
def _compute_total_budget(self):
for record in self:
record.x_total_budget = sum(record.x_line_ids.mapped('x_amount'))
Atrybut store=True jest istotny, jeśli chcesz filtrować, sortować lub agregować po tym polu — nieprzechowywane pola obliczane nie mogą być używane w domenach ORM i w widokach opartych na SQL.
Dodawanie pola przez API
Jeżeli tworzysz pola zdalnie (np. przez XML-RPC w skrypcie konfiguracyjnym), możesz dodać pole Monetary przez model ir.model.fields:
models.execute_kw(ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_budget',
'field_description': 'Budget',
'model_id': model_id,
'ttype': 'monetary',
'currency_field': 'currency_id',
'state': 'manual',
}]
)
To jedna z opcji automatyzacji konfiguracji Odoo dostępnych przez API i często wykorzystywana przy masowych wdrożeniach lub migracjach danych.
Dobre praktyki
Najlepsze praktyki — co warto stosować
1. Nigdy nie używaj Float do wartości pieniężnych
To najważniejsza zasada: jeśli pole reprezentuje cenę, kwotę, sumę, budżet lub jakąkolwiek wartość w walucie, użyj fields.Monetary. Float nie rozumie waluty i nie zastosuje właściwych reguł zaokrąglania.
2. Zawsze jawnie deklaruj pole waluty
Nie polegaj na domyślnym currency_id, jeśli nie istnieje. Określ parametr currency_field i zadeklaruj odpowiadające pole Many2one do res.currency, by uniknąć cichego fallbacku w środowiskach wielowalutowych.
3. Ustaw domyślną walutę
Dla pól wewnętrznych, które zwykle korzystają z waluty firmy, ustaw domyślną wartość: default=lambda self: self.env.company.currency_id. Dzięki temu nowe rekordy od razu mają przypisaną walutę i poprawne formatowanie.
4. Use store=True dla pól obliczanych, które będą wyszukiwane
Jeżeli pole obliczane Monetary ma być filtrowane lub sortowane, ustaw store=True. Brak przechowania to częsty powód, dla którego niestandardowe pulpitowe widoki czy raporty nie działają jak oczekiwano.
5. Stosuj currency.round() w obliczeniach pośrednich
W wieloetapowych kalkulacjach używaj self.currency_id.round(value) po istotnych krokach, a nie tylko na końcu. To zapobiega kumulacji błędów zmiennoprzecinkowych i rozbieżnościom w sumach.
6. Bądź świadomy przy raportach wielowalutowych
Agregując kwoty z rekordów w różnych walutach, nie sumuj surowych liczb. Najpierw skonwertuj je do jednej waluty przy pomocy res.currency.compute() lub wygeneruj raport w walucie docelowej — mieszanie walut w sumowaniu daje wyniki pozornie poprawne na poziomie pól, ale bezwartościowe finansowo.
Typowe pułapki
Najczęstsze błędy — na co uważać
Błąd 1: brak pola waluty
Najczęstszy problem to zapomnienie o deklaracji powiązanego pola Many2one do waluty. Jeśli currency_id nie istnieje, Odoo czasem cicho zastosuje walutę firmy, a innym razem zwróci błąd. Twórz pole waluty zawsze razem z polem Monetary.
Błąd 2: dwa pola walutowe używające tej samej waluty, choć powinny mieć różne
Jeśli na jednym modelu znajdują się dwa pola Monetary, które mają reprezentować różne waluty (np. cena klienta w EUR i koszt dostawcy w USD), nie mogą one korzystać z jednego wspólnego currency_id. Każde pole powinno mieć własne odniesienie do res.currency, inaczej jedno ustawienie nadpisze oba pola.
Błąd 3: rozbieżności zaokrągleń przy agregowaniu walut
Sumowanie pól Monetary z różnych walut bez konwersji da wyniki, które będą wyglądać na błędne — dodawanie EUR do USD bez przeliczenia to częsty powód problemów w raportach. Zawsze normalizuj do jednej waluty przed agregacją.
Błąd 4: porównania float w zapytaniach ORM
Wyszukiwanie pola Monetary przy użyciu dokładnej równości (np. amount = 10.0) może nie znaleźć rekordów ze względu na sposób przechowywania floatów. Lepiej używać zakresów (>=/<=) z tolerancją lub zaokrąglać wartości przed porównaniem w logice Pythona.
Błąd 5: nieuwzględnianie zaokrąglania przy imporcie danych
Przy imporcie CSV lub przez XML-RPC wartości Monetary są zapisywane tak, jak je dostarczysz — bez automatycznego zaokrąglenia. Jeśli dane źródłowe mają więcej miejsc po przecinku niż docelowa waluta, zapis będzie technicznie poprawny, ale wyświetlanie i sumy mogą się różnić. Stosuj zaokrąglenie waluty w skryptach importu przed wysłaniem danych do Odoo.
Podsumowanie
Pole Monetary wydaje się proste, ale zawiera istotne reguły dotyczące waluty, precyzji i zaokrągleń. To właśnie powiązanie z rekordem waluty czyni je niezawodnym narzędziem dla danych finansowych: poprawne formatowanie, spójne zaokrąglenia i świadome wyświetlanie w całym systemie.
Poprawne stosowanie tego pola — zawsze z jawnie zadeklarowaną walutą i bez zastępowania go zwykłym Floatem — uchroni Cię przed wieloma subtelnymi błędami, które trudno zdiagnozować w produkcji. Model danych Odoo opiera się na tym mechanizmie nie bez powodu.
Niezależnie czy korzystasz z przewodnika dla deweloperów, modyfikujesz standardowy moduł czy tworzysz rozwiązanie od podstaw, prawidłowe użycie pól Monetary to fundament stabilnego i przewidywalnego przetwarzania wartości pieniężnych w Odoo.
Potrzebujesz wsparcia przy wdrożeniu Odoo?
W Dasolo wspieramy przedsiębiorstwa na każdym etapie wdrożenia i optymalizacji Odoo. Pomagamy zaprojektować czysty model danych, strategię pól walutowych, wdrożyć obsługę wielu walut i przeprowadzić pełne uruchomienie systemu — mamy kompetencje zarówno funkcjonalne, jak i techniczne.
Masz pytania dotyczące pól Monetary lub innych aspektów implementacji Odoo? Chętnie pomożemy. Skontaktuj się z nami porozmawiajmy o Twoim projekcie.