Odoo’da paket satan herkesin, örneğin aksesuarlı ve montajlı bir ölçüm cihazı, donanım ve kurulumuyla birlikte bir çalışma istasyonu ya da birden fazla hizmetten oluşan bir bakım paketi satanların, teklifte aynı anda iki şeye ihtiyacı vardır: Bileşim siparişte yer almalıdır ki depo, teknik ekip ve faturalama neyin teslim edileceğini bilsin. Müşteriye verilen çıktıda ise her durumda her kalemin her birim fiyatıyla görünmesi istenmez.
OCA modülü sale_product_set, ürün setlerini (satış için ürün ağaçları) tek tıkla bir teklife eklemenizi sağlar. Baskı görünümüne ise karışmaz. Bunun için Sale Product Set Layout modülünü (sale_product_set_layout) geliştirdik. Modül Odoo 19 için yazılmıştır ve AGPL-3 lisansı altında özgür yazılım olarak GitHub’da ücretsiz sunulmaktadır:
github.com/Alex0176/odoo-sale-product-set-layout
Bu yazının içeriği
Sorun: Baskı görünümüne etkisi olmayan ürün setleri
Odoo’da ürün seti, miktarlarıyla birlikte hazırlanmış bir ürün listesidir. On kalemi tek tek aramak yerine teklifte Set ekle seçeneğini kullanırsınız ve tüm satırlar tek seferde gelir. Bu zaman kazandırır ve unutulan kalemleri önler. Şu depodaki sale_product_set modülü tam olarak bunu yapar: OCA/sale-workflow ; setlerin kendisi ise şu depodaki product_set modülünden gelir: OCA/product-attribute.
Pratikte üç eksik kalır:
- Belirgin bir gruplama yok. Set satırları teklifte birbirinden bağımsız kalemler olarak yer alır. Ekledikten sonra hangi satırların birbirine ait olduğu görülmez ve setin toplam tutarı hiçbir yerde yazmaz.
- Çıktıya etkisi yok. Bazen müşteri her kalemi fiyatıyla görmeli, bazen yalnızca paket fiyatını, bazen de tek bir satırı. Bu, her teklifte elle yapılacak bir karar değil, set veya müşteri bazında verilecek bir karardır.
- Fiyat düzeltmeleri hoş değil. Bir paketin liste fiyatları toplamından farklı sabit bir fiyatı olacaksa bir düzeltme satırı gerekir. Bu satır hesaplamaya aittir, ancak müşterinin kâğıdında yer alması şart değildir.
Odoo 19, bunu temiz biçimde çözmek için gerekli yapı taşlarını sunar: Teklif satırlarında bölümler ve alt bölümler, Fiyatları gizle ve Bileşimi gizle seçenekleriyle (collapse_prices ve collapse_composition alanları). Modül, kendi yazdırma biçimini icat etmek yerine ürün setini bu standart seçeneklerle bağlar.
Modül ne yapar
- Seti bölüm olarak ekleme. Set ekle sihirbazı, setin adını taşıyan bir bölüm satırı oluşturur, örneğin 2 × Messkit Standard, ve set satırlarını bunun altına yerleştirir. Set içindeki bölümler alt bölüm olur.
- Sete özel yazdırma modu. Setin nasıl yazdırılacağı sette bir kez belirlenir. Ekleme sırasında sihirbazda bu mod yalnızca o durum için değiştirilebilir.
- Satırları gizleme. Tek tek satırlar Yazdırma (sette: Gizli) olarak işaretlenebilir. Bu satırlar toplama dahil edilir, ancak ne yazdırılır ne de müşteri portalında gösterilir. Bu işaret tekliften faturaya taşınır.
- Set satırı başına sabit fiyat. Bir set satırı, fiyat listesindeki fiyatın yerini alan sabit bir birim fiyat taşıyabilir, örneğin gizlenen bir fiyat düzeltmesi için.
Modül olmadan (yalnızca sale_product_set) | sale_product_set_layout ile |
|---|---|
| Set satırları birbirinden bağımsız kalemler olarak | Set adını taşıyan bölüm satırı, miktar adın içinde, satırlar altında |
| Setteki bölümler bölüm olarak kalır ve yapıyı bozar | Setteki bölümler alt bölüm olur (line_subsection) |
| Baskı görünümü her zaman aynı | Sete özel yazdırma modu, ekleme sırasında değiştirilebilir |
| Fiyat düzeltmesi teklifte ve faturada görünür | Satır gizlenebilir, ancak toplama dahil edilir |
| Fiyat yalnızca fiyat listesinden | İsteğe bağlı olarak set satırı başına sabit fiyat |
| Düzen teklifte biter | Düzen ve gizleme; teklif, sipariş, fatura ve müşteri portalı için geçerlidir |
Üç yazdırma modunun ayrıntıları
Yazdırma modu, ürün setinde bulunan bir seçim alanıdır: print_mode. Ekleme sırasında bölüm satırının iki standart anahtarına dönüştürülür. Bunun için modülde tek ve kolay okunur bir eşleme bulunur:
# Druckmodus -> (collapse_prices, collapse_composition) der Abschnittszeile
PRINT_MODE_FLAGS = {
"detailed": (False, False),
"total_only": (True, False),
"summary": (False, True),
}
Mod (print_mode) | Sette görünüm | Odoo’daki standart seçenek | Baskı görünümü |
|---|---|---|---|
detailed | Her satır fiyatıyla | yok | Bölüm, tüm satırlar tutarlarıyla |
total_only | Fiyatsız satırlar, yalnızca set toplamı | collapse_prices (Fiyatları gizle) | Toplamlı bölüm, satırlar yalnızca ad ve miktarla |
summary | Set toplamıyla yalnızca tek satır | collapse_composition (Bileşimi gizle) | Toplamlı yalnızca bölüm satırı |
Modül, kendi baskı biçimini değil Odoo 19’un standart seçeneklerini kullandığı için teklif, sipariş onayı, fatura ve müşteri portalı özelleştirilmiş rapor şablonlarına gerek kalmadan çalışır. Bölüm seçeneklerini dikkate alan kendi rapor şablonları olanlar da aynı davranışı elde eder.
Gizlenen satırlar ve sabit fiyat
Sık görülen bir durum: Bir paketin, tekil fiyatların toplamının altında kalan bir paket fiyatı vardır. Tekil fiyatları çarpıtmak yerine sabit fiyatlı bir düzeltme satırı eklenir (örneğin set başına −50,00 €) ve bu satır gizlenir. Arka uçta fatura izlenebilirdir, çıktıda ise paket fiyatı yer alır.
Anlaşılması açısından önemli: hide_in_report işareti hesaplamada hiçbir şeyi değiştirmez. Miktar, fiyat, indirim, vergi, toplamlar ve muhasebe kayıtları değişmeden standart Odoo tarafından oluşturulur. Satır yalnızca çıktıdan ve portaldan eksiktir.
- Ürün setinde: Gizli ve isteğe bağlı olarak Sabit fiyat ile birlikte Birim fiyat bulunan set satırı.
- Teklif ve fatura satırlarında: Yazdırma sütunu (
hide_in_report), setsiz normal satırlar için de. - Aktarım: Faturayı siparişten oluştururken işaret fatura satırına aktarılır. Bir teklif kopyalanırken korunur.
Teknik uygulama
Modül bilerek küçük tutulmuştur: Mevcut modelleri genişletir ve yalnızca Odoo’nun hangi satırların yazdırılacağına ve bir setin nasıl ekleneceğine karar verdiği yerleri geçersiz kılar. Yeni rapor şablonu, yeni model ve SQL müdahalesi yoktur.
Genişletilen modeller ve alanlar
| Model | Ek |
|---|---|
product.set | insert_as_section (Boolean, varsayılan açık) ve print_mode (seçim, zorunlu alan, varsayılan detailed) alanları |
product.set.line | hide_in_report, fixed_price, price_unit alanları; prepare_sale_order_line_values() bunları teklif satırına iletir |
sale.product.set.wizard | insert_as_section, section_name, print_mode alanları; _prepare_order_lines() bölüm satırını oluşturur |
sale.order / sale.order.line | hide_in_report alanı (copy=True ile); _get_order_lines_to_report() gizlenen satırları atlar; _prepare_invoice_line() işareti iletir |
account.move / account.move.line | hide_in_report alanı; _get_move_lines_to_report(), _get_detailed_lines() ve _get_subsection_lines() gizlenen satırları filtreler; _get_aml_vals() işareti rapor verilerine taşır |
Tekliflerde gizleme
Odoo 19, yazdırılacak teklif satırlarını _get_order_lines_to_report() içinde belirler. Modül oraya bir filtre ekler. Rapor şablonuna müdahale etmek gerekmez: Hem teklifin rapor şablonu (sale/report/ir_actions_report_templates.xml) hem de portal şablonu (sale/views/sale_portal_templates.xml) bu metodu çağırır:
class SaleOrder(models.Model):
_inherit = "sale.order"
def _get_order_lines_to_report(self):
return super()._get_order_lines_to_report().filtered(
lambda line: not line.hide_in_report
)
Faturaya aktarım
Teklif satırından fatura satırı oluşturulurken (_prepare_invoice_line()) işaret kopyalanır. Odoo 19’da fatura tarafında satırların rapora ulaşabileceği birkaç yol vardır: ana liste, bir bölümün satırları ve bir alt bölümün satırları. Bu nedenle modül birden fazla yerde filtreler (belgede _get_move_lines_to_report(), satırda _get_detailed_lines() ve _get_subsection_lines()); aksi takdirde gizlenen bir satır diğer yollardan birinden yeniden ortaya çıkabilirdi:
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
hide_in_report = fields.Boolean(string="Hidden in report")
def _get_aml_vals(self, hide_taxes):
res = super()._get_aml_vals(hide_taxes)
res["hide_in_report"] = self.hide_in_report
return res
def _get_detailed_lines(self, children_lines, direct_children_lines, subsection_lines):
res = super()._get_detailed_lines(children_lines, direct_children_lines, subsection_lines)
return [vals for vals in res if not vals.get("hide_in_report")]
def _get_subsection_lines(self, subsection_line, children_lines):
res = super()._get_subsection_lines(subsection_line, children_lines)
return [vals for vals in res if not vals.get("hide_in_report")]
Sihirbaz: Bağımsız satırlar yerine bölüm
sale_product_set içindeki sale.product.set.wizard sihirbazı, teklif satırlarını _prepare_order_lines() içinde üretir. Modül bu metodu geçersiz kılar. Bölüm olarak ekle kapalıysa özgün metodu çağırır, davranış OCA modülündeki gibi kalır. Aksi hâlde önce iki standart anahtarı taşıyan bölüm satırı oluşturulur, ardından set satırları gelir. Set içindeki bölümler, ana yapıyı bozmasınlar diye alt bölüm olur:
def _prepare_order_lines(self):
if not self.insert_as_section:
return super()._prepare_order_lines()
sequence = self._get_max_sequence() + 1
collapse_prices, collapse_composition = PRINT_MODE_FLAGS[self.print_mode or "detailed"]
order_lines = [Command.create({
"display_type": "line_section",
"name": self.section_name or self.product_set_id.name,
"sequence": sequence,
"collapse_prices": collapse_prices,
"collapse_composition": collapse_composition,
})]
for set_line in self._get_lines():
sequence += 1
values = set_line.prepare_sale_order_line_values(self.order_id, self.quantity, sequence)
if values.get("display_type") == "line_section":
values["display_type"] = "line_subsection"
order_lines.append(Command.create(values))
return order_lines
Görünmeyen kararlar
- Sihirbazda ayrı hesaplama metotları. Bölüm adı, yazdırma modu ve Bölüm olarak ekle alanlarının her birinin kendi
computemetodu vardır (store=True,readonly=False). Üçü de aynı metodu kullansaydı, bir alan için açıkça verilen değer diğerlerini sıfırlardı. Bu, özellikle sihirbaz kod veya testlerden değerlerle oluşturulduğunda göze çarpar. - Miktarlı bölüm adı. Miktar 1’den farklıysa adın içinde yer alır (2 × Messkit Standard, ondalık ayırıcı olarak virgülle); böylece çıktıda bunun iki set olduğu, miktarı iki katı olan tek bir set olmadığı anlaşılır.
- Sabit fiyat yalnızca ürün satırlarında.
prepare_sale_order_line_values(),hide_in_reportveprice_unitdeğerlerini yalnızca set satırı bir bölüm ya da not satırı değilse ayarlar. - Kendi çözümümüz yerine standart Odoo. Yazdırma modu ve gizleme, Odoo 19’un mevcut kancalarını ve alanlarını kullanır. Bu, güncellemeleri yönetilebilir kılar: Odoo rapor şablonunu değiştirirse, metotlar var olduğu sürece modül geçerli kalır.
İstenmeyen yan etkiler olmadan kurulum
Ön koşullar: Odoo 19.0, sale_product_set (OCA/sale-workflow, 19.0 dalı) ve product_set (OCA/product-attribute, 19.0 dalı). Modülün kendisi sale_management, sale_product_set ve account modüllerine bağlıdır.
OCA modüllerinde auto_install tuzağı. Birçok OCA deposu auto_install içeren modüller barındırır. Bunların bağımlılıkları mevcut olur olmaz Odoo, bir sonraki kurulumda veya güncellemede onları sormadan birlikte kurar. Bu nedenle bir üretim ortamının addons_path yoluna OCA deposunun tamamını koyan kişi, kimsenin istemediği modüllerle karşılaşabilir. OCA/product-attribute için bu, örneğin purchase_product_template_tags modülünü kapsar. Yaklaşımımız:
- İki OCA deposunu sığ olarak klonlayın (
git clone --depth 1 --branch 19.0). - Ayrı bir dizinde yalnızca gereken iki modülü sembolik bağlantıyla bağlayın ve depoların tamamını değil, bu dizini
addons_pathyoluna ekleyin. - Bir test veritabanına kurun ve bu sırada hangi modüllerin kurulu durumuna geçtiğini kontrol edin.
- Ancak bundan sonra üretime kurun; çağrının çalışan hizmetle HTTP portu için yarışmaması adına
--workers=0ile.
odoo-bin -c /etc/odoo/odoo.conf -d <datenbank> -i sale_product_set_layout --stop-after-init
Yeni alanlar ve görünümler kurulumdan sonra veritabanındadır. Hizmet çok işlemli modda çalışıyorsa worker’lar değişen registry’yi veritabanı sinyali üzerinden yeniden yükler. Modülün Python dosyalarındaki değişiklikler ancak hizmet yeniden başlatıldıktan sonra etkili olur.
Adım adım kullanım
- Set oluşturma: Satış → Ürünler → Ürün setleri. Sete ad verin, Bölüm olarak ekle ve Yazdırma modu seçeneklerini belirleyin, ürünleri miktarlarıyla girin. Düzeltme satırlarına Gizli ve Sabit fiyat atayın.
- Teklifte: Set ekle düğmesine tıklayın, set ve miktarı seçin. Bölüm adı ve yazdırma modu eklemeden önce değiştirilebilir.
- Tek tek satırları gizleme: Teklifte veya faturada Yazdırma sütununu etkinleştirin.
- Kontrol: Önizlemeyi veya PDF’yi açın. Müşteriler portalda aynı görünümü görür.
Testler
Modül, üretime geçmeden önce bir test veritabanına karşı çalıştırdığımız altı otomatik test ile birlikte gelir:
- Bölüm olarak ekleme, yazdırma modu ve alt bölüm dahil
- Gizlenen satır, yazdırılacak satırlarda yer almaz
- Tek satır modu (
summary) - Bölümsüz ekleme, özgün davranış
- Fatura,
hide_in_reportişaretini devralır, satır_get_move_lines_to_report()içinde yer almaz, bölüm toplamı ise onu hâlâ içerir - Yalnızca toplam fiyat modundaki fatura, gizli satırı bölüm satırlarında göstermez, bölüm toplamı eksiksiz kalır
odoo-bin -d <testdatenbank> -i sale_product_set_layout \
--test-enable --test-tags=/sale_product_set_layout --stop-after-init
Almanca çeviri i18n/de.po dosyasındadır.
Sınırlar ve hukuki not
- Yalnızca düzen. Modül, Odoo’nun fiyatları, indirimleri veya vergileri nasıl hesapladığını değiştirmez. Yalnızca nelerin yazdırılıp portalda gösterileceğini değiştirir.
- Gizlenen satırlar arka uçta görünür. İşaret çıktıyı ve portalı etkiler, çalışanların ekran görünümünü değil.
- Fatura bilgilerini kontrol edin. Gizlenen satırlar toplama dahil edilir, ancak belgede yer almaz. Bunun, ülkenizdeki faturalara yönelik gereklilikler, örneğin teslim edilen mal veya sunulan hizmetlerin zorunlu açıklaması, ile uyumlu olup olmadığını faturalarda kullanmadan önce mali müşavirinizle netleştirmelisiniz. Tekliflerde gizleme daha sorunsuzdur. Bu yazı hukuki danışmanlık değildir.
- Odoo 19 için test edildi. Modülü Odoo 19.0 Community için geliştirdik ve test ettik. Odoo 19’da bu biçimde bulunan bölüm seçeneklerini kullanır. Diğer sürümler için denemedik.
Sık sorulan sorular
Odoo 19 zaten bölümleri destekliyorsa modüle neden ihtiyacım var?
Bölüm seçenekleri Odoo’nun kendisinde mevcut. Eksik olan, ürün setiyle bağlantıydı: Bir set, sette tanımlı yazdırma moduyla otomatik olarak bölüm şeklinde eklenmeliydi. Modül olmadan her eklemeden sonra bölüm satırını elle oluşturup yapılandırmanız gerekir.
“Fiyatları gizle” ile “Bileşimi gizle” arasındaki fark nedir?
“Fiyatları gizle” (collapse_prices) satırları bırakır, ancak tutar göstermez, yalnızca bölümdeki toplamı gösterir. “Bileşimi gizle” (collapse_composition) satırları tamamen gizler, müşteri yalnızca toplamlı bölümü görür.
Gizlenen bir satır fatura toplamına dahil mi?
Evet. Gizlenen satırlar toplamda, vergide ve muhasebe kaydında kalır. İşaret yalnızca çıktıdaki ve müşteri portalındaki görünümü etkiler.
Ekleme sırasında yazdırma modunu hâlâ değiştirebilir miyim?
Evet. Mod, varsayılan olarak setten gelir; sihirbazda ilgili ekleme için değiştirilebilir. Bu sırada setin kendisi değişmez.
Bölüm olarak ekle seçeneğini kapatırsam ne olur?
O zaman sihirbaz, OCA modülü sale_product_set içindeki gibi davranır: Set satırları bölümsüz eklenir.
Modül beklenmedik şekilde başka modüller kuruyor mu?
Modülün kendisi kurmaz. Ancak OCA depolarının tamamını addons_path yoluna koyarsanız auto_install modülleri devreye girebilir. Bu nedenle yalnızca gereken iki modülü bağlayın (bkz. Kurulum bölümü).
Modül Odoo Enterprise ile çalışır mı?
Modül yalnızca Community modüllerine (sale_management, account) ve iki OCA modülüne bağlıdır. Biz bunu Odoo 19 Community üzerinde kullanıyoruz. Enterprise üzerinde test etmedik.
Modül hangi lisans altındadır?
AGPL-3 altındadır. Değişiklikler aynı lisans altında kaldığı sürece onu kullanabilir, uyarlayabilir ve paylaşabilirsiniz. Hatalar ve iyileştirme önerileri şu adreste bir issue olarak memnuniyetle karşılanır: GitHub deposu .
Biricon IT Services'ten destek
Küçük ve orta ölçekli işletmeler için Odoo kurulumlarına bakıyoruz: kurulumdan özelleştirmelere ve geçişlere, süregelen işletime kadar. Odoo 19’da ürün setlerini, teklif şablonlarını veya raporları iş akışlarınıza uyarlamak istiyorsanız ya da bunun gibi bir modülü inceleyip üretim ortamınıza güvenle almak istiyorsanız, size memnuniyetle destek oluruz. Diğer kendi geliştirmelerimizi blogumuzda bulabilirsiniz, örneğin Taslak faturaları birleştirme ve Banka ödemelerini otomatik olarak faturalarla eşleştirme.
Durum: 2 Kasım 2026. Modül sürümü 19.0.1.0.0. Görsellerdeki örnek veriler serbestçe seçilmiştir, tutarlar net.