GRT Sales Business Category
Dokumentasi teknis dan fungsional modul grt_sales_business_category.
Modul ini memperluas proses Sales, CRM, Customer, Invoice, Payment, dan API frontend agar seluruh transaksi penjualan dapat:
- mengikuti
Business Category - membawa
Analytic Accountyang konsisten - memakai approval 2 tahap sebelum
confirm - mendukung draft order dari frontend
- menghitung ongkir otomatis berbasis wilayah customer
- menghasilkan analisis perilaku customer per business category
Ringkasan Modul
Nama modul: grt_sales_business_category
Dependensi utama:
sale_managementsale_crmaccountcontactsasa_wilayahgrt_business_category_basegrt_crm_business_category
Hook yang dijalankan:
post_init_hook
Fungsi hook:
- membersihkan menu/action legacy sales team yang sudah tidak dipakai
- melakukan backfill
customer_qr_refpadares.partneryang belum punya referensi QR
Tujuan Bisnis
Modul ini menggabungkan beberapa kebutuhan bisnis dalam satu layer:
- kategorisasi transaksi sales berdasarkan business category
- kontrol approval quotation sebelum menjadi sales order
- integrasi analytic account ke invoice dan payment
- dukungan frontend order dengan ongkir berbasis wilayah
- segmentasi customer berdasarkan perilaku pembelian
Struktur Fitur
Area utama modul ini:
- Sales Order business category dan approval
- Frontend sales API
- Frontend shipping rule
- Partner QR dan wilayah ongkir
- Customer behavior analysis
- Integrasi accounting ke invoice dan payment
- Security berbasis effective business category
- Report sales per business category
Model yang Ditambah atau Diubah
Model yang di-extend:
sale.ordersale.order.lineres.partnercrm.business.categorycrm.teamaccount.moveaccount.move.lineaccount.paymentaccount.payment.register
Model baru:
sale.frontend.shipping.rulecustomer.behavior.analysiscustomer.behavior.configcustomer.behavior.segmentcustomer.behavior.dashboardcustomer.behavior.recompute.wizard
Sales Order Logic
Implementasi utama ada di models/sale_order.py.
Field Tambahan di sale.order
business_category_idanalytic_account_idis_frontend_orderfrontend_kecamatan_idcustomer_qr_refcustomer_wilayah_kecamatan_idsales_team_leader_idapproval_statesales_leader_approved_by_idsales_leader_approved_dateaccounting_approved_by_idaccounting_approved_datecan_approve_sales_leadercan_approve_accounting
Business Category dan Team Sales
Logic yang berlaku:
Business Categorydapat terisi otomatis dariTeam SalesBusiness Categorydapat terisi otomatis dariCRM Opportunity- jika
Team Salesdiganti dan tidak lagi cocok dengan category, field tim akan dibersihkan - domain
Team Salesdibatasi ke tim yangbusiness_category_id-nya sesuai dan company-nya sama
Validasi yang berlaku:
- business category harus berasal dari company yang sama
- analytic account harus berasal dari company yang sama
- team harus punya business category
- team yang dipilih harus cocok dengan business category order
- jika order terkait opportunity, business category order harus sama dengan business category opportunity
- user hanya boleh membuat/mengedit order dalam effective business category miliknya
- user harus terdaftar di tim sales yang dipilih
Analytic Account pada Sales Order
analytic_account_id adalah field related ke business_category_id.analytic_account_id.
Fungsi:
- menampilkan analytic account yang dipakai order
- memastikan invoice yang dibuat dari Sales Order membawa analytic account yang sama
- memastikan invoice line juga menerima analytic account order
Catatan:
- field ini
readonly - perubahan seharusnya dilakukan pada master
Business Category
Approval 2 Tahap
State approval:
draftwaiting_sales_leaderwaiting_accountingapprovedrejected
Alur approval:
- user membuat quotation
- user klik
Submit Approval - Sales Team Leader approve
- Accounting Manager approve
- sistem menjalankan
action_confirm
Aturan penting:
- quotation tidak bisa dikonfirmasi langsung jika
approval_state != approved - hanya draft/sent quotation yang dapat masuk alur approval
- Sales Team Leader diambil dari
crm.team.sale_team_leader_id, fallback kecrm.team.user_id - reject hanya bisa dilakukan saat masih dalam proses approval
- reset approval hanya bisa dilakukan saat state order masih
draftatausent
Frontend Order pada Sales Order
is_frontend_order dipakai sebagai penanda bahwa order dibuat dari frontend/API.
Jika is_frontend_order = True, maka sistem:
- mengambil wilayah ongkir dari customer
- mengisi
frontend_kecamatan_id - mencari shipping rule berdasarkan wilayah
- menghitung total unit item order
- membuat atau memperbarui line ongkir otomatis
Jika is_frontend_order = False, line ongkir frontend akan dibersihkan pada onchange.
Wilayah Customer vs Wilayah Frontend
customer_wilayah_kecamatan_id
- related field ke
partner_id.commercial_partner_id.shipping_wilayah_kecamatan_id - menunjukkan wilayah ongkir default milik customer
- sifatnya master data customer
frontend_kecamatan_id
- field transaksi pada Sales Order
- berfungsi sebagai snapshot wilayah yang dipakai saat kalkulasi ongkir frontend
- dipakai untuk mencari
sale.frontend.shipping.rule
Kenapa dipisah:
Wilayah Customeradalah data masterWilayah Frontendadalah data transaksi/order- jika wilayah customer berubah di kemudian hari, order lama tetap menyimpan wilayah yang dipakai saat transaksi dibuat
- pemisahan ini juga memberi ruang jika suatu saat dibutuhkan override wilayah khusus order
Logic Ongkir Frontend
Method utama:
_apply_frontend_shipping_rule_get_frontend_shipping_total_units_onchange_frontend_shipping_preview_needs_frontend_shipping_recalc
Flow:
- customer dipilih atau order dibuat lewat API frontend
- order ditandai sebagai frontend order
- sistem membaca
partner.shipping_wilayah_kecamatan_id frontend_kecamatan_iddiisi- sistem mencari
sale.frontend.shipping.ruleberdasarkan:company_iddanwilayah_kecamatan_id - sistem menghitung total unit item selain line ongkir
- sistem membuat atau update line ongkir dengan:
is_frontend_shipping_line = True
Kondisi error:
- customer belum punya
Wilayah Ongkir - tidak ada rule ongkir untuk wilayah itu
- produk ongkir pada rule tidak saleable
- tarif per unit tidak valid
- total unit produk tidak lebih dari 0
Logic pada sale.order.line
sale.order.line ditambah field:
is_frontend_shipping_line
Logic tambahan:
- invoice line mewarisi
analytic_account_iddari order - create/write/unlink line akan memicu recalculation ongkir frontend jika order terkait adalah frontend order
- recalculation di-skip jika context
skip_frontend_shipping_recalcaktif
Partner Logic
Implementasi utama ada di models/res_partner.py.
Field Tambahan di res.partner
customer_id_displaycustomer_qr_refshipping_wilayah_kecamatan_idcustomer_segment_idbehavior_business_category_idlast_sale_datebehavior_currency_idtotal_sales_amountsales_frequencydays_since_last_ordercustomer_behavior_analysis_ids
Customer QR Reference
Tujuan:
- setiap commercial partner memiliki referensi QR unik
- referensi ini dipakai oleh frontend API untuk pencarian customer
Logic:
- saat partner dibuat, sistem menjalankan
_ensure_customer_qr_ref - hanya commercial partner yang diberi QR ref
- child contact tidak diberi QR ref sendiri
- sequence memakai kode
res.partner.customer.qr.ref - ada SQL constraint unik pada
customer_qr_ref - saat install atau upgrade, partner lama yang belum punya QR ref akan di-backfill
Wilayah Ongkir Customer
Field:
shipping_wilayah_kecamatan_id
Fungsi:
- menjadi sumber utama penentuan rule ongkir frontend
- dipakai pada Sales Order frontend sebagai basis
frontend_kecamatan_id - jika field ini kosong, sistem mencoba mengisi otomatis dari alamat partner
- pencocokan dilakukan ke master
wilayah.kecamatanberdasarkancity, lalu fallback kestreetdanstreet2 - auto-fill hanya terjadi jika ditemukan tepat satu kecamatan yang cocok dan tidak menimpa input manual yang sudah ada
Customer Behavior Summary di Partner
Partner menampilkan ringkasan behavior hasil analisis:
- segment customer
- business category analysis yang dipakai
- last sale date
- total sales amount
- sales frequency
- days since last order
Logic:
- partner mengambil analysis terbaru yang masih berada dalam scope business category user
- jika
behavior_business_category_iddiisi dan masih accessible, summary akan mengikuti category itu - jika tidak ada category behavior yang valid, sistem memilih analysis terbaru yang accessible
Customer Behavior Logic
Implementasi utama ada di:
models/customer_behavior_analysis.pymodels/customer_behavior_config.pymodels/customer_behavior_segment.pymodels/customer_behavior_dashboard.pymodels/customer_behavior_recompute_wizard.py
Tujuan
Fitur ini mengelompokkan customer ke segmen perilaku berdasarkan histori sale.order per business category.
Model customer.behavior.config
Fungsi:
- menyimpan threshold hari untuk segmentasi customer
- menyimpan minimum nilai transaksi yang dianggap valid
- aktif per business category
Field utama:
repeat_daysat_risk_daysinactive_daysdormant_dayslost_daysmin_transactionactive
Validasi:
- urutan threshold harus
repeat <= at_risk <= inactive <= dormant <= lost - hanya satu config aktif per business category
- nama config unik per business category
Action:
Run AnalysisRecompute Wizard
Model customer.behavior.segment
Fungsi:
- menyimpan definisi segment per config
Kode segment yang dipakai sistem:
repeatreactivatedat_riskinactivedormantlost
Validasi:
- kombinasi
codedanconfig_idharus unik
Logic tambahan:
- row legacy tanpa
config_idakan dinonaktifkan saat init
Model customer.behavior.analysis
Fungsi:
- menyimpan hasil analisis customer per tanggal dan business category
Data yang dihitung:
- tanggal pembelian terakhir
- tanggal pembelian sebelumnya
- selisih hari sejak pembelian terakhir
- jumlah order
- total nilai order
- average order value
- segment customer
Sumber data:
sale.orderdengan kondisi:state = salepartner_idterisiamount_total >= min_transactionbusiness_category_idsama dengan config
Logic segmentasi:
lostjika hari sejak pembelian terakhir >lost_daysdormantjika >dormant_daysinactivejika >inactive_daysat_riskjika >at_risk_daysreactivatedjika total order > 1 dan gap pembelian sebelumnya melebihi threshold inactive- selain itu
repeat
Catatan:
- analisis disimpan unik per
partner,analysis_date, danbusiness_category - saat recompute untuk partner tertentu, data analisis di tanggal itu akan di-reset dulu
- jika partner belum punya
behavior_business_category_id, sistem bisa mengisi category dari hasil analysis
Model customer.behavior.dashboard
Fungsi:
- wizard/dashboard ringkas untuk melihat KPI perilaku customer per business category
Metrik yang dihitung:
- total customers
- repeat customers
- reactivated customers
- at risk customers
- inactive customers
- dormant customers
- lost customers
- total revenue
- retention rate
- churn rate
- at risk rate
Action tombol dashboard:
- buka seluruh analysis
- buka retained customer
- buka churn customer
- buka at risk customer
Model customer.behavior.recompute.wizard
Fungsi:
- menjalankan recompute analysis untuk customer tertentu atau semua customer
Mode:
selectedall
Output:
- setelah recompute, user diarahkan ke daftar
customer.behavior.analysis
CRM Logic
crm.business.category
Implementasi utama ada di models/crm_business_category.py.
Tambahan field:
analytic_account_id
Fungsi:
- menjadi sumber analytic account default untuk Sales Order, Invoice, dan Payment
Validasi:
- analytic account harus berasal dari company yang sama dengan business category
crm.team
Implementasi utama ada di models/crm_team.py.
Tambahan field:
sale_team_leader_id
Fungsi:
- user ini menjadi approver tahap pertama untuk Sales Order
Accounting Logic
Account Move
Implementasi utama ada di models/account_move.py.
Field tambahan:
business_category_idanalytic_account_id
Logic:
- saat business category berubah, analytic account otomatis mengikuti category
- invoice/diverse move harus konsisten dengan company
- analytic account invoice harus sama dengan analytic account yang dikonfigurasi pada business category
Account Move Line
Logic create:
- jika move adalah customer/vendor invoice atau refund
- jika move punya analytic account
- jika line bukan receivable/payable
- dan line belum punya analytic account
maka line akan otomatis mewarisi analytic_account_id dari parent move.
Account Payment
Implementasi utama ada di models/account_payment.py.
Field tambahan:
business_category_idanalytic_account_id
Logic:
- saat business category payment berubah, analytic account otomatis mengikuti category
- saat
action_post, receivable/payable lines pada jurnal payment akan diisi analytic account jika masih kosong
Validasi:
- business category harus satu company dengan payment
- analytic account harus satu company dengan payment
- analytic account payment harus sama dengan analytic account business category
Account Payment Register
Implementasi utama ada di models/account_payment_register.py.
Logic:
- saat wizard membuat payment dari invoice
- jika semua invoice memiliki business category yang sama, category tersebut dibawa ke payment
- jika semua invoice memiliki analytic account yang sama, analytic account tersebut dibawa ke payment
Frontend Shipping Rule
Implementasi utama ada di models/frontend_shipping_rule.py.
Model:
sale.frontend.shipping.rule
Field utama:
company_idwilayah_kecamatan_idshipping_product_idshipping_price_per_kgactivenamecompute
Aturan:
- satu company hanya boleh punya satu rule per wilayah
- produk ongkir harus berasal dari company yang sama jika produk punya company
- tarif per unit harus lebih besar dari 0
Fungsi:
- menjadi master tarif ongkir frontend berbasis wilayah
- dipakai saat membuat draft Sales Order frontend
Frontend API / Service
Implementasi utama ada di controllers/main.py.
Controller ini menyediakan JSON API berbasis session Odoo.
Helper Internal
Service/helper internal yang dipakai controller:
- parsing payload JSON-RPC atau payload langsung
- autentikasi session
- pencarian customer berdasarkan
customer_qr_refataucustomer_id - builder payload QR
- builder aging bucket accounting
- helper create draft order beserta lines dan ongkir frontend
Model Authentication
Alur:
- frontend memanggil endpoint login
- Odoo membuat session
- endpoint lain memakai session yang sama
Endpoint login:
POST /api/sales/authenticate
Endpoint Master Data
POST /api/sales/productsPOST /api/sales/payment-terms
Fungsi:
- mengambil produk saleable
- mengambil daftar payment term
Endpoint QR Customer
POST /api/sales/customer-qr-by-idPOST /api/sales/customer-qr-payload-by-idPOST /api/sales/customer-qr-payload-by-ref
Fungsi:
- mengambil QR reference customer
- membentuk payload QR dalam format
refataujson
Endpoint Customer Detail dan Accounting Summary
POST /api/sales/customer-detail-by-qrPOST /api/sales/customer-accounting-summary-by-qr
Data yang dikembalikan:
- identitas customer
- alamat dan wilayah ongkir
- payment term
- total receivable/payable
- aging receivable/payable
Endpoint Histori Sales Order
POST /api/sales/orders-by-qr
Fungsi:
- mengambil histori Sales Order berdasarkan
customer_qr_ref
Data penting:
- nomor order
- tanggal order
- commitment date
- amount total
- state
- approval state
Endpoint Create Draft Order Frontend
POST /api/sales/draft-orderPOST /api/sales/draft-order/bon-keringPOST /api/sales/draft-order/bon-partusPOST /api/sales/draft-order/bon-reguler
Logic umum create draft order:
- partner bisa ditentukan lewat
partner_idataucustomer_qr_ref - jika keduanya diberikan, keduanya harus merujuk ke customer yang sama
commitment_datewajib adapayment_term_idwajib adaorder_linewajib berisi minimal satu item- order otomatis dibuat sebagai
is_frontend_order = True - optional field
team_iddanbusiness_category_idboleh ikut dikirim - setelah order dan lines dibuat, sistem menjalankan
_apply_frontend_shipping_rule
Response sukses biasanya memuat:
sale_order_idnamestateamount_totalline_countterms_and_conditionsis_frontend_orderwilayah_idwilayah_nameshipping_product_idshipping_product_nameshipping_price_per_kg
Variasi Bon
Endpoint bon kering, bon partus, dan bon reguler bekerja seperti draft order biasa, tetapi menambahkan note default tertentu ke terms and conditions.
Security dan Access Rules
Security utama ada di:
security/security.xmlsecurity/ir.model.access.csvsecurity/ir.rule.csv
Group Tambahan
Sales Team Leader
Group ini meng-implikasikan sales_team.group_sale_salesman.
Record Rule Umum
Sebagian besar akses dibatasi berdasarkan:
user.company_idsuser.effective_business_category_ids
Model yang dibatasi:
sale.ordercrm.teamcrm.business.categorycustomer.behavior.analysiscustomer.behavior.configcustomer.behavior.segmentaccount.moveaccount.paymentaccount.move.line
Prinsip akses:
- sales user hanya melihat data pada effective business category-nya
- sales manager mendapat akses lebih luas tetapi tetap dibatasi company dan effective business category
- admin modul tetap dibatasi company dan effective business category
- hanya super admin (UID 1) yang dapat bypass penuh
View dan Menu
View utama yang ditambahkan:
- Sales Order form/search/tree
- Partner form
- CRM Business Category form
- CRM Team form
- Invoice form
- Payment form
- Frontend Shipping Rule tree/form/search
- Customer Behavior Analysis tree/form/pivot/graph/search
- Customer Behavior Config tree/form
- Customer Behavior Segment tree/form
- Customer Behavior Dashboard form
- Customer Behavior Recompute Wizard form
- Sales by Business Category report pivot/graph
Menu utama di Sales:
Business CategoriesFrontend Shipping RulesCustomer BehaviorBehavior DashboardCustomer SegmentsCustomer Behavior ConfigSales by Category
Report
Report tambahan:
Sales by Business Category
Tersedia dalam:
- tree
- pivot
- graph
Dimensi utama:
- business category
- team sales
- state
- bulan order
Measure utama:
amount_total- jumlah order
Otomasi yang Berjalan di Modul
Daftar automation penting:
- partner baru otomatis diberi
customer_qr_ref - partner lama di-backfill QR ref saat install/upgrade
- Sales Order mengambil business category dari team/opportunity jika tersedia
- Sales Order membawa analytic account dari business category
- frontend order otomatis hitung ongkir
- perubahan line pada frontend order otomatis recalculate ongkir
- invoice dari Sales Order membawa business category dan analytic account
- invoice line mewarisi analytic account dari invoice/order
- payment wizard mewarisi business category dan analytic account dari invoice
- payment journal line receivable/payable diisi analytic account saat posting
- customer behavior analysis dapat dihitung ulang dari config atau wizard
- dashboard menghitung KPI customer behavior secara dinamis
Constraint dan Konsistensi Data
Constraint penting yang aktif:
customer_qr_refunik- satu frontend shipping rule unik per company dan wilayah
- tarif ongkir per unit harus > 0
- analytic account business category harus satu company
- analytic account invoice/payment harus cocok dengan business category
- satu config aktif per business category
- threshold segment behavior harus urut
- segment code unik per config
- analisis customer unik per partner, tanggal, dan business category
- team sales harus cocok dengan business category order
- user hanya boleh bekerja dalam effective business category miliknya
File Penting
__manifest__.py__init__.pymodels/sale_order.pymodels/res_partner.pymodels/frontend_shipping_rule.pymodels/crm_business_category.pymodels/crm_team.pymodels/account_move.pymodels/account_payment.pymodels/account_payment_register.pymodels/customer_behavior_analysis.pymodels/customer_behavior_config.pymodels/customer_behavior_segment.pymodels/customer_behavior_dashboard.pymodels/customer_behavior_recompute_wizard.pycontrollers/main.pyviews/sale_order_views.xmlviews/res_partner_views.xmlviews/frontend_shipping_rule_views.xmlviews/customer_behavior_analysis_views.xmlviews/customer_behavior_config_views.xmlviews/customer_behavior_dashboard_views.xmlviews/customer_behavior_recompute_wizard_views.xmlviews/customer_behavior_segment_views.xmlviews/sale_report_business_category_views.xmlsecurity/security.xmlsecurity/ir.model.access.csvsecurity/ir.rule.csv
Catatan Operasional
- jika field baru model muncul di code tetapi belum ada di database, jalankan upgrade module
- fitur frontend shipping sangat bergantung pada
shipping_wilayah_kecamatan_iddi customer dan mastersale.frontend.shipping.rule - approval Sales Order akan memblokir confirm jika proses approval belum selesai
- untuk penggunaan API frontend, pastikan session/cookie Odoo dipertahankan di client
Referensi Tambahan
frontend_sales.mdfrontend_shipping_rule_test.mdsales_customer_behaviour.md
Update Akses Ketat (2026-04-16)
Perubahan ini menutup celah akses lintas business category ketika user menghapus filter list default seperti My Quotations.
Ringkasan perubahan:
- ditambahkan guard domain di level model
sale.order._searchagar query selalu dibatasi ke:company_idusereffective_business_category_idsuser
- ditambahkan
sale.order.check_access_ruleuntuk memberi pesanAccess deniedsaat ada akses record lintas category - bypass untuk
base.group_systemdi validasi operasional diubah menjadi hanyaSUPERUSER_ID(UID 1)
Implikasi operasional:
- admin modul sales tetap tidak bisa lintas category jika tidak didaftarkan ke team/business category terkait
- hanya super admin (UID 1) yang dapat bypass penuh
- perilaku akses Sales selaras dengan kebijakan ketat modul CRM
Langkah deploy:
- restart service Odoo
- upgrade modul
grt_sales_business_category - uji user non-super-admin dengan menghapus filter list default dan pastikan data lintas category tidak muncul
- uji akses langsung record lintas category dan pastikan muncul peringatan akses
Update Sales Order Type (2026-04-23)
Perubahan baru ini menambahkan pemisahan Tipe Sales Order untuk business category tertentu tanpa memaksa semua category memakai field yang sama.
Ringkasan perubahan:
- ditambahkan toggle
crm.business.category.use_sale_order_type - ditambahkan field
sale.order.sale_order_type - field
sale_order_typehanya muncul jika business category mengaktifkan fitur tersebut - jika business category aktif memakai tipe Sales Order, maka
sale_order_typewajib diisi - nilai
sale_order_typedibawa keaccount.move.sale_order_type - ditambahkan field related stored
account.move.line.sale_order_typeagar filter laporan piutang atau journal item bisa dilakukan langsung dari data accounting
Nilai sale_order_type yang tersedia:
regulerkeringpartusperalatansilase
Perubahan endpoint frontend:
- endpoint draft order sekarang menerima field
sale_order_type - jika business category yang dipakai frontend mengaktifkan tipe Sales Order, field
sale_order_typewajib dikirim - endpoint
/api/sales/draft-order/bon-keringmewajibkansale_order_type = kering - endpoint
/api/sales/draft-order/bon-partusmewajibkansale_order_type = partus - endpoint
/api/sales/draft-order/bon-regulermewajibkansale_order_type = reguler - endpoint
/api/sales/draft-order/non-ongkirtidak mengunci satu type tertentu, selama nilainya valid - response create draft order dan histori order sekarang mengembalikan
sale_order_typedansale_order_type_label
Catatan operasional:
- untuk business category yang tidak mengaktifkan fitur ini, field
sale_order_typeakan tetap kosong - setelah deploy, lakukan upgrade module agar field baru muncul di database
- bila ada report piutang custom yang membaca
account.move.line, fieldsale_order_typesudah siap dipakai sebagai filter tambahan
Update Default Ongkir dan Commission per Type (2026-04-23)
Behavior default frontend order sekarang mengikuti sale_order_type.
Default ongkir:
reguler-> memakai ongkir sesuai wilayahkering-> memakai ongkir sesuai wilayahpartus-> memakai ongkir sesuai wilayahperalatan-> default tanpa ongkirsilase-> memakai ongkir sesuai wilayah
Default sales commission:
reguler-> kena fee sales commissionkering-> kena fee sales commissionpartus-> kena fee sales commissionperalatan-> tidak kena fee sales commissionsilase-> tidak kena fee sales commission
Catatan integrasi endpoint:
- endpoint draft order generic akan mengikuti default di atas berdasarkan
sale_order_type - endpoint
bon-kering,bon-partus, danbon-regulertetap memaksa type masing-masing dan otomatis mengikuti default ongkir/commission yang sesuai - endpoint
non-ongkirtetap memaksa transaksi tanpa ongkir, tetapi response sekarang tetap menampilkansale_order_typedan status default commission jika modul commission terpasang