Odoo의 공식 문서에는 새로운 구성 옵션을 추가하는 방법에 대한 상세한 가이드가 부족한 경우가 많습니다. 본 글에서는 Odoo 시스템에 자체적인 설정 항목을 정의하고 적용하는 과정을 단계별로 설명합니다.
1. 모델 정의: TransientModel 상속
새로운 설정을 만들기 위해서는 먼저 res.config.settings를 상속받는 모델을 정의해야 합니다. 이 모델은 마법사(Wizard) 역할을 하는 TransientModel이어야 하며, 데이터는 영구적으로 저장되지 않고 일시적으로만 보관됩니다. 따라서 실제 값이 DB에 반영되려면 별도의 로직이 필요합니다.
class CustomAppSettings(models.TransientModel):
_inherit = 'res.config.settings'
_name = 'custom.app.settings'
Odoo는 필드 이름의 접두사를 기반으로 네 가지 유형의 설정 처리 방식을 자동으로 구분합니다.
1.1 기본값(Default) 설정
필드 이름이 default_로 시작하면 해당 값은 지정된 모델의 특정 필드에 대한 전역 기본값으로 설정됩니다. 예를 들어, default_customer_id라는 필드를 만들면 sale.order 모델의 partner_id 필드 기본값이 변경됩니다.
class CustomAppSettings(models.TransientModel):
_inherit = 'res.config.settings'
_name = 'custom.app.settings'
# sale.order 모델의 partner_id 필드 기본값 설정
default_partner_id = fields.Many2one(
'res.partner',
string='Default Customer',
default_model='sale.order'
)
1.2 그룹(Group) 권한 설정
부울(Boolean) 타입 필드이며 이름이 group_로 시작할 경우, 이는 사용자 그룹 권한과 연동됩니다. 체크박스가 활성화되면 implied_group에 정의된 그룹이 group에 정의된 상위 그룹에 포함됩니다. 이는 기능 접근 제어를 위해 유용하게 사용됩니다.
class CustomAppSettings(models.TransientModel):
_inherit = 'res.config.settings'
_name = 'custom.app.settings'
group_manager_access = fields.Boolean(
string='Enable Manager Access',
group='base.group_user', # 기본 대상 그룹
implied_group='custom_app.group_managers' # 부여될 권한 그룹
)
1.3 모듈(Module) 설치 설정
부울 타입 필드이며 이름이 module_로 시작하면, 해당 옵션이 활성화될 때 지정된 이름의 모듈을 자동으로 설치하거나 제거합니다. 주의할 점은 필드명에서 module_ 접두사를 제외한 나머지가 실제 모듈 기술 이름(Technical Name)과 일치해야 한다는 것입니다.
class CustomAppSettings(models.TransientModel):
_inherit = 'res.config.settings'
_name = 'custom.app.settings'
# 'advanced_inventory' 모듈을 제어함
module_advanced_inventory = fields.Boolean(string='Install Advanced Inventory')
1.4 기타 커스텀 저장 로직
위 세 가지 유형에 속하지 않는 일반 필드는 기본적으로 저장 시 무시됩니다. 이를 해결하기 위해서는 set_<field_name> 형식의 메서드를 구현하여 값을 수동으로 저장해야 합니다. 또한 초기 화면 표시를 위해 get_default_<method_name> 메서드를 사용할 수 있습니다.
예를 들어, 현재 사용자의 회사 정보를 설정 화면에서 수정하고 저장하는 경우:
class CustomAppSettings(models.TransientModel):
_inherit = 'res.config.settings'
_name = 'custom.app.settings'
company_legal_name = fields.Char(string='Legal Company Name')
company_tax_id = fields.Char(string='Tax ID')
@api.model
def get_default_company_info(self, fields):
"""
설정 화면 로드 시 현재 회사 정보로 필드 초기화
"""
current_company = self.env.user.company_id
return {
'company_legal_name': current_company.name,
'company_tax_id': current_company.vat,
}
@api.multi
def set_company_info(self):
"""
저장 버튼 클릭 시 회사 레코드 업데이트
"""
for record in self:
target_company = self.env.user.company_id
target_company.write({
'name': record.company_legal_name,
'vat': record.company_tax_id,
})
return True
2. 뷰(View) 정의
모델 정의 후, 설정 화면을 위한 XML 뷰를 작성해야 합니다. 일반적인 폼 뷰와 유사하지만, 상단에 저장 및 취소 버튼을 명시적으로 배치해야 합니다.
<record id="view_custom_settings_form" model="ir.ui.view">
<field name="name">Custom App Settings</field>
<field name="model">custom.app.settings</field>
<field name="arch" type="xml">
<form string="Custom Configuration" class="oe_form_configuration">
<header>
<button string="Save" type="object" name="execute" class="oe_highlight"/>
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
</header>
<group string="Company Information">
<field name="company_legal_name"/>
<field name="company_tax_id"/>
</group>
<group string="Access Control">
<field name="group_manager_access"/>
</group>
<group string="Modules">
<field name="module_advanced_inventory"/>
</group>
</form>
</field>
</record>
3. 액션(Action) 및 메뉴(Menu) 등록
마지막으로 뷰를 연결하는 윈도우 액션을 생성하고, 설정 메뉴 하위에 해당 액션을 호출하는 메뉴 항목을 추가합니다.
<!-- 액션 정의 -->
<record id="action_custom_settings" model="ir.actions.act_window">
<field name="name">Custom App Configuration</field>
<field name="res_model">custom.app.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module': 'custom_app'}</field>
</record>
<!-- 메뉴 등록 (설정 > 일반 설정 하위 등) -->
<menuitem id="menu_custom_settings"
name="Custom App Settings"
parent="base.menu_config"
action="action_custom_settings"/>
위 절차를 완료하면 Odoo의 '설정' 메뉴 내에 새로운 탭이나 섹션으로 커스텀 설정 항목이 나타나며, 사용자가 입력한 값은 각 유형에 따라 적절히 처리되어 시스템 전체에 반영됩니다.