# Nâng Cấp Hệ Thống Odoo Hiện Có

Nhiệm vụ: nâng cấp an toàn hệ thống Odoo hiện có qua kế thừa module, mở rộng và tích hợp, theo thực hành brownfield để giữ nguyên chức năng cũ và thêm năng lực mới.

## Mục tiêu
### Chính
- Nâng cấp module hiện có mà không phá vỡ chức năng
- Thêm tính năng bằng pattern kế thừa/mở rộng
- Đảm bảo khả năng nâng cấp & bảo trì
- Tuân chuẩn OCA để tương thích cộng đồng
- Giảm gián đoạn hệ thống, giữ toàn vẹn dữ liệu

### Tiêu chí thành công
- Chức năng mới hoạt động trơn tru cùng hệ thống cũ
- Chức năng cũ không bị ảnh hưởng
- Nâng cấp an toàn nâng cấp, dễ bảo trì
- Code tuân chuẩn chất lượng OCA
- Kiểm thử bao quát cả mới và cũ

## Điều kiện tiên quyết
### Phân tích hệ thống
- Đánh giá module/tùy chỉnh hiện có
- Phân tích ảnh hưởng & dependencies
- Yêu cầu nghiệp vụ cho nâng cấp
- Ràng buộc kỹ thuật (schema, tích hợp)
- Ảnh hưởng tới người dùng / đào tạo

### Môi trường
- Môi trường giống production để test
- Backup đầy đủ hệ thống hiện tại
- VCS để theo dõi thay đổi
- Quy trình kiểm thử hồi quy

## Chiến lược nâng cấp

### 1) Mở rộng model (thêm field)
```python
# models/sale_order_extension.py
from odoo import api, fields, models

class SaleOrderExtension(models.Model):
    _inherit = 'sale.order'

    custom_reference = fields.Char(string='Custom Reference',
                                   help='Tham chiếu nội bộ')
    special_instructions = fields.Text(string='Special Instructions',
                                       help='Hướng dẫn xử lý đặc biệt')
    approval_required = fields.Boolean(
        string='Approval Required',
        compute='_compute_approval_required',
        store=True,
        help='Đơn hàng cần phê duyệt')
    approval_status = fields.Selection([
        ('pending', 'Pending Approval'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected')
    ], string='Approval Status', default='pending')

    @api.depends('amount_total', 'partner_id.approval_limit')
    def _compute_approval_required(self):
        for order in self:
            limit = order.partner_id.approval_limit or 1000.0
            order.approval_required = order.amount_total > limit
```

**Mở rộng view**
```xml
<odoo>
  <record id="view_order_form_extension" model="ir.ui.view">
    <field name="name">sale.order.form.extension</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form"/>
    <field name="arch" type="xml">
      <xpath expr="//field[@name='client_order_ref']" position="after">
        <field name="custom_reference"/>
      </xpath>
      <xpath expr="//notebook" position="inside">
        <page string="Special Instructions" name="special_instructions">
          <group><field name="special_instructions" nolabel="1"/></group>
        </page>
      </xpath>
      <xpath expr="//header" position="inside">
        <button name="action_request_approval" type="object"
                string="Request Approval" class="oe_highlight"
                invisible="not approval_required or approval_status != 'pending'"/>
        <button name="action_approve" type="object"
                string="Approve" class="oe_highlight"
                invisible="approval_status != 'pending'"
                groups="sales_team.group_sale_manager"/>
        <field name="approval_status" widget="statusbar"
               invisible="not approval_required"/>
      </xpath>
    </field>
  </record>

  <record id="view_quotation_tree_extension" model="ir.ui.view">
    <field name="name">sale.order.tree.extension</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_quotation_tree"/>
    <field name="arch" type="xml">
      <xpath expr="//field[@name='amount_total']" position="after">
        <field name="approval_status" invisible="not approval_required"/>
      </xpath>
    </field>
  </record>
</odoo>
```

### 2) Mở rộng logic nghiệp vụ
```python
class SaleOrderLogicExtension(models.Model):
    _inherit = 'sale.order'

    @api.model
    def create(self, vals):
        if vals.get('partner_id'):
            partner = self.env['res.partner'].browse(vals['partner_id'])
            if partner.requires_special_handling:
                vals['special_instructions'] = 'Handle with care - VIP customer'
        order = super().create(vals)
        if order.approval_required:
            order._send_approval_notification()
        return order

    def write(self, vals):
        if 'state' in vals and vals['state'] == 'sale':
            for order in self:
                if order.approval_required and order.approval_status != 'approved':
                    raise UserError(_('Cannot confirm order without approval'))
        result = super().write(vals)
        if vals.get('approval_status') == 'approved':
            self._send_approval_confirmation()
        return result

    def action_confirm(self):
        for order in self:
            if order.approval_required and order.approval_status != 'approved':
                raise UserError(_('Order requires approval before confirmation'))
            if order.amount_total > 50000 and not order.special_instructions:
                raise UserError(_('Large orders require special instructions'))
        return super().action_confirm()

    def _send_approval_notification(self):
        template = self.env.ref('custom_module.approval_request_template')
        for order in self:
            template.send_mail(order.id, force_send=True)

    def _send_approval_confirmation(self):
        template = self.env.ref('custom_module.approval_confirmation_template')
        for order in self:
            template.send_mail(order.id, force_send=True)
```

### 3) Bổ sung workflow
```python
class SaleOrderWorkflow(models.Model):
    _inherit = 'sale.order'

    def action_request_approval(self):
        self.ensure_one()
        if not self.approval_required:
            raise UserError(_('This order does not require approval'))
        self.approval_status = 'pending'
        self._send_approval_notification()
        return {'type': 'ir.actions.client', 'tag': 'display_notification',
                'params': {'message': _('Approval request sent to management'),
                           'type': 'success'}}

    def action_approve(self):
        self.ensure_one()
        if not self.env.user.has_group('sales_team.group_sale_manager'):
            raise AccessError(_('Only sales managers can approve orders'))
        self.approval_status = 'approved'
        self._send_approval_confirmation()
        if self.state == 'draft':
            self.action_confirm()
        return {'type': 'ir.actions.client', 'tag': 'display_notification',
                'params': {'message': _('Order approved successfully'),
                           'type': 'success'}}

    def action_reject(self):
        self.ensure_one()
        if not self.env.user.has_group('sales_team.group_sale_manager'):
            raise AccessError(_('Only sales managers can reject orders'))
        self.approval_status = 'rejected'
        return {'type': 'ir.actions.client', 'tag': 'display_notification',
                'params': {'message': _('Order rejected'), 'type': 'warning'}}
```

### 4) Nâng cấp tích hợp
```python
class SaleOrderIntegration(models.Model):
    _inherit = 'sale.order'

    external_system_id = fields.Char(string='External System ID',
                                     help='ID hệ thống ngoài')
    last_sync_date = fields.Datetime(string='Last Sync Date', readonly=True)
    sync_status = fields.Selection([
        ('pending', 'Pending Sync'),
        ('synced', 'Synced'),
        ('error', 'Sync Error')
    ], string='Sync Status', default='pending')

    def action_sync_external_system(self):
        external_api = self.env['external.api.connector']
        for order in self:
            try:
                data = {
                    'order_number': order.name,
                    'customer_id': order.partner_id.external_id,
                    'total_amount': order.amount_total,
                    'order_lines': [{
                        'product_code': line.product_id.default_code,
                        'quantity': line.product_uom_qty,
                        'price': line.price_unit,
                    } for line in order.order_line]
                }
                response = external_api.create_order(data)
                order.write({
                    'external_system_id': response.get('id'),
                    'sync_status': 'synced',
                    'last_sync_date': fields.Datetime.now()
                })
            except Exception as e:
                order.write({
                    'sync_status': 'error',
                    'last_sync_date': fields.Datetime.now()
                })
                _logger.error(f'Sync error for order {order.name}: {e}')

    @api.model
    def cron_sync_orders(self):
        pending = self.search([
            ('sync_status', '=', 'pending'),
            ('state', 'in', ['sale', 'done'])
        ])
        pending.action_sync_external_system()
```

### 5) Nâng cấp báo cáo
```python
class SaleReportExtension(models.Model):
    _inherit = 'sale.report'

    approval_required = fields.Boolean(string='Approval Required', readonly=True)
    approval_status = fields.Selection([
        ('pending', 'Pending Approval'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected')
    ], string='Approval Status', readonly=True)

    def _select_additional_fields(self):
        res = super()._select_additional_fields()
        res['approval_required'] = "s.approval_required"
        res['approval_status'] = "s.approval_status"
        return res

    def _group_by_sale(self):
        res = super()._group_by_sale()
        res += ", s.approval_required, s.approval_status"
        return res
```

### 6) Nâng cấp bảo mật
```xml
<odoo>
  <record id="sale_order_approval_rule" model="ir.rule">
    <field name="name">Sale Order: Approval Access</field>
    <field name="model_id" ref="sale.model_sale_order"/>
    <field name="domain_force">[
      '|',
      ('approval_required', '=', False),
      '|',
      ('user_id', '=', user.id),
      ('approval_status', '=', 'approved')
    ]</field>
    <field name="groups" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
  </record>

  <record id="sale_order_manager_rule" model="ir.rule">
    <field name="name">Sale Order: Manager Full Access</field>
    <field name="model_id" ref="sale.model_sale_order"/>
    <field name="domain_force">[(1, '=', 1)]</field>
    <field name="groups" eval="[(4, ref('sales_team.group_sale_manager'))]"/>
  </record>
</odoo>
```

## Thực hành tốt nhất khi triển khai
### 1) Chiến lược kế thừa
- Ưu tiên `_inherit`, tránh sửa core
- Giảm thay đổi phá vỡ tương thích
- Tài liệu rõ dependencies

### 2) An toàn dữ liệu
```python
def safe_field_addition(self):
    if not hasattr(self, 'new_field'):
        self._add_field('new_field', fields.Char('New Field'))
    if not self.search([('new_field', '!=', False)]):
        self._migrate_existing_data()
```

### 3) Chiến lược kiểm thử
```python
class TestSaleOrderEnhancement(TransactionCase):
    def setUp(self):
        super().setUp()
        self.partner = self.env.ref('base.res_partner_1')
        self.product = self.env.ref('product.product_product_3')

    def test_approval_workflow(self):
        order = self.env['sale.order'].create({
            'partner_id': self.partner.id,
            'order_line': [(0, 0, {
                'product_id': self.product.id,
                'product_uom_qty': 100,
                'price_unit': 1000,
            })]
        })
        self.assertTrue(order.approval_required)
        order.action_approve()
        self.assertEqual(order.approval_status, 'approved')

    def test_backward_compatibility(self):
        order = self.env['sale.order'].create({
            'partner_id': self.partner.id,
            'order_line': [(0, 0, {
                'product_id': self.product.id,
                'product_uom_qty': 1,
                'price_unit': 100,
            })]
        })
        order.action_confirm()
        self.assertEqual(order.state, 'sale')
```

### 4) Lưu ý di chuyển
```python
def migrate_existing_orders(cr, version):
    if not version:
        return
    cr.execute("""
        ALTER TABLE sale_order
        ADD COLUMN IF NOT EXISTS custom_reference VARCHAR(255),
        ADD COLUMN IF NOT EXISTS approval_required BOOLEAN DEFAULT FALSE,
        ADD COLUMN IF NOT EXISTS approval_status VARCHAR(20) DEFAULT 'pending'
    """)
    cr.execute("""
        UPDATE sale_order
        SET approval_required = TRUE
        WHERE amount_total > 1000
    """)
    cr.execute("""
        CREATE INDEX IF NOT EXISTS sale_order_approval_status_idx
        ON sale_order(approval_status)
    """)
```

## Đảm bảo chất lượng
**Pre-deployment**
- [ ] Test chức năng cũ còn hoạt động
- [ ] Tính năng mới đúng đặc tả
- [ ] Script migration đã test
- [ ] Quy tắc bảo mật đúng
- [ ] Đánh giá tác động hiệu suất
- [ ] Cập nhật tài liệu
- [ ] Chuẩn bị tài liệu đào tạo
- [ ] Quy trình rollback sẵn sàng

**Giám sát hiệu suất (ví dụ)**
```python
def monitor_performance_impact(self):
    import time
    start_time = time.time()
    orders = self.env['sale.order'].search([])
    for order in orders[:100]:
        order.action_confirm()
    duration = time.time() - start_time
    if duration > 60:
        _logger.warning(f'Performance degradation detected: {duration}s')
```

## Chiến lược triển khai
### Staged rollout
1) Dev: test/validate đầy đủ  
2) Staging: UAT  
3) Prod: rollout theo pha, giám sát  
4) Hậu triển khai: giám sát hiệu suất, lấy feedback

### Rollback
- Backup DB trước deploy
- Quy trình rollback code
- Khôi phục dữ liệu
- Kế hoạch truyền thông người dùng

Nhớ: Thành công phụ thuộc kiểm thử kỹ, kế hoạch cẩn trọng và giữ tương thích ngược khi thêm giá trị mới.