2017-07-14 93 views
2

嘗試重新使用此Odoo 9插件時https://www.odoo.com/apps/modules/9.0/purchase_recurring_orders/ | github code到Odoo 10,我現在面臨的以下問題:這裏可以看到從Odoo 9移植到Odoo 10的插件 - ProgrammingError:無法修改類型'account.tax'

File "/usr/lib/python2.7/dist-packages/odoo/custom_addons/purchase_recurring_orders/models/recurring_orders.py", line 310, in generate_initial_order 
order = self.create_order(self.start_date, agreement_lines) 
... 
ProgrammingError: can't adapt type 'account.tax' 

完整的錯誤:

Traceback (most recent call last): 
    File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 640, in _handle_exception 
    return super(JsonRequest, self)._handle_exception(exception) 
    File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 677, in dispatch 
    result = self._call_function(**self.params) 
    File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 333, in _call_function 
    return checked_call(self.db, *args, **kwargs) 
    File "/usr/lib/python2.7/dist-packages/odoo/service/model.py", line 101, in wrapper 
    return f(dbname, *args, **kwargs) 
    File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 326, in checked_call 
    result = self.endpoint(*a, **kw) 
    File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 935, in __call__ 
    return self.method(*args, **kw) 
    File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 506, in response_wrap 
    response = f(*args, **kw) 
    File "/usr/lib/python2.7/dist-packages/odoo/addons/web/controllers/main.py", line 889, in call_button 
    action = self._call_kw(model, method, args, {}) 
    File "/usr/lib/python2.7/dist-packages/odoo/addons/web/controllers/main.py", line 877, in _call_kw 
    return call_kw(request.env[model], method, args, kwargs) 
    File "/usr/lib/python2.7/dist-packages/odoo/api.py", line 681, in call_kw 
    return call_kw_multi(method, model, args, kwargs) 
    File "/usr/lib/python2.7/dist-packages/odoo/api.py", line 672, in call_kw_multi 
    result = method(recs, *args, **kwargs) 
    File "/usr/lib/python2.7/dist-packages/odoo/custom_addons/purchase_recurring_orders/models/recurring_orders.py", line 310, in generate_initial_order 
    order = self.create_order(self.start_date, agreement_lines) 
    File "/usr/lib/python2.7/dist-packages/odoo/custom_addons/purchase_recurring_orders/models/recurring_orders.py", line 259, in create_order 
    order_line_obj.create(order_line_vals) 
    File "/usr/lib/python2.7/dist-packages/odoo/addons/purchase/models/purchase.py", line 557, in create 
    line = super(PurchaseOrderLine, self).create(values) 
    File "/usr/lib/python2.7/dist-packages/odoo/models.py", line 3830, in create 
    record = self.browse(self._create(old_vals)) 
    File "/usr/lib/python2.7/dist-packages/odoo/models.py", line 3987, in _create 
    field.write(self.with_context(rel_context), vals[name]) 
    File "/usr/lib/python2.7/dist-packages/odoo/fields.py", line 2402, in write 
    link(act[2]) 
    File "/usr/lib/python2.7/dist-packages/odoo/fields.py", line 2367, in link 
    cr.execute(query, (records.ids, list(sub_ids), tuple(records.ids))) 
    File "/usr/lib/python2.7/dist-packages/odoo/sql_db.py", line 154, in wrapper 
    return f(self, *args, **kwargs) 
    File "/usr/lib/python2.7/dist-packages/odoo/sql_db.py", line 231, in execute 
    res = self._obj.execute(query, params) 
•ProgrammingError: can't adapt type 'account.tax' 

到目前爲止,我沒有修改任何比團體利用別人。 這是recurring_orders的代碼:

# -*- coding: utf-8 -*- 
############################################################################## 
# 
# Cybrosys Technologies Pvt. Ltd. 
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). 
# Author: Jesni Banu(<http://www.cybrosys.com>) 
# you can modify it under the terms of the GNU LESSER 
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. 
# 
# It is forbidden to publish, distribute, sublicense, or sell copies 
# of the Software or modified copies of the Software. 
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. 
# 
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE 
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. 
# If not, see <http://www.gnu.org/licenses/>. 
# 
############################################################################### 
from datetime import timedelta 
from datetime import datetime 
from dateutil.relativedelta import relativedelta 
from openerp import models, fields, api, exceptions, _ 
import openerp.addons.decimal_precision as dp 
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT 


class Agreement(models.Model): 
    _name = 'purchase.recurring_orders.agreement' 
    _inherit = ['mail.thread'] 
    _description = "Recurring orders agreement" 

    @api.model 
    def __get_next_term_date(self, date, unit, interval): 
     if unit == 'days': 
      return date + timedelta(days=interval) 
     elif unit == 'weeks': 
      return date + timedelta(weeks=interval) 
     elif unit == 'months': 
      return date + relativedelta(months=interval) 
     elif unit == 'years': 
      return date + relativedelta(years=interval) 

    @api.multi 
    def _compute_next_expiration_date(self): 
     for agreement in self: 
      if agreement.prolong == 'fixed': 
       agreement.next_expiration_date = agreement.end_date 
      elif agreement.prolong == 'unlimited': 
       now = fields.Date.from_string(fields.Date.today()) 
       date = self.__get_next_term_date(
        fields.Date.from_string(agreement.start_date), 
        agreement.prolong_unit, agreement.prolong_interval) 
       while date < now: 
        date = self.__get_next_term_date(
         date, agreement.prolong_unit, 
         agreement.prolong_interval) 
       agreement.next_expiration_date = date 
      else: 
       agreement.next_expiration_date = self.__get_next_term_date(
        fields.Date.from_string(agreement.last_renovation_date or 
              agreement.start_date), 
        agreement.prolong_unit, agreement.prolong_interval) 

    def _default_company_id(self): 
     company_model = self.env['res.company'] 
     company_id = company_model._company_default_get('purchase') 
     return company_model.browse(company_id.id) 

    name = fields.Char(
     string='Name', size=100, index=True, required=True, 
     help='Name that helps to identify the agreement') 
    number = fields.Char(
     string='Agreement number', index=True, size=32, copy=False, 
     help="Number of agreement. Keep empty to get the number assigned by a " 
      "sequence.") 
    active = fields.Boolean(
     string='Active', default=True, 
     help='Unchecking this field, quotas are not generated') 
    partner_id = fields.Many2one(
     comodel_name='res.partner', string='Supplier', index=True, 
     change_default=True, required=True, 
     help="Supplier you are making the agreement with") 
    company_id = fields.Many2one(
     comodel_name='res.company', string='Company', required=True, 
     help="Company that signs the agreement", default=_default_company_id) 
    start_date = fields.Date(
     string='Start date', index=True, copy=False, 
     help="Beginning of the agreement. Keep empty to use the current date") 
    prolong = fields.Selection(
     selection=[('recurrent', 'Renewable fixed term'), 
        ('unlimited', 'Unlimited term'), 
        ('fixed', 'Fixed term')], 
     string='Prolongation', default='unlimited', 
     help="Sets the term of the agreement. 'Renewable fixed term': It sets " 
      "a fixed term, but with possibility of manual renew; 'Unlimited " 
      "term': Renew is made automatically; 'Fixed term': The term is " 
      "fixed and there is no possibility to renew.", required=True) 
    end_date = fields.Date(
     string='End date', help="End date of the agreement") 
    prolong_interval = fields.Integer(
     string='Interval', default=1, 
     help="Interval in time units to prolong the agreement until new " 
      "renewable (that is automatic for unlimited term, manual for " 
      "renewable fixed term).") 
    prolong_unit = fields.Selection(
     selection=[('days', 'days'), 
        ('weeks', 'weeks'), 
        ('months', 'months'), 
        ('years', 'years')], 
     string='Interval unit', default='years', 
     help='Time unit for the prolongation interval') 
    agreement_line = fields.One2many(
     comodel_name='purchase.recurring_orders.agreement.line', 
     inverse_name='agreement_id', string='Agreement lines') 
    order_line = fields.One2many(
     comodel_name='purchase.order', copy=False, inverse_name='agreement_id', 
     string='Orders', readonly=True) 
    renewal_line = fields.One2many(
     comodel_name='purchase.recurring_orders.agreement.renewal', copy=False, 
     inverse_name='agreement_id', string='Renewal lines', readonly=True) 
    last_renovation_date = fields.Date(
     string='Last renovation date', 
     help="Last date when agreement was renewed (same as start date if not " 
      "renewed)") 
    next_expiration_date = fields.Date(
     compute="_compute_next_expiration_date", string='Next expiration date') 
    state = fields.Selection(
     selection=[('empty', 'Without orders'), 
        ('first', 'First order created'), 
        ('orders', 'With orders')], 
     string='State', readonly=True, default='empty') 
    renewal_state = fields.Selection(
     selection=[('not_renewed', 'Agreement not renewed'), 
        ('renewed', 'Agreement renewed')], 
     string='Renewal state', readonly=True, default='not_renewed') 
    notes = fields.Text('Notes') 

    _sql_constraints = [ 
     ('number_uniq', 'unique(number)', 'Agreement number must be unique !'), 
    ] 

    @api.constrains('start_date', 'end_date') 
    def _check_dates(self): 
     for record in self: 
      if record.end_date and record.end_date < record.start_date: 
       raise exceptions.Warning(
        _('Agreement end date must be greater than start date')) 

    @api.model 
    def create(self, vals): 
     if not vals.get('start_date'): 
      vals['start_date'] = fields.Date.today() 
     if not vals.get('number'): 
      vals['number'] = self.env['ir.sequence'].get(
       'purchase.r_o.agreement.sequence') 
     return super(Agreement, self).create(vals) 

    @api.multi 
    def write(self, vals): 
     value = super(Agreement, self).write(vals) 
     if (any(vals.get(x) is not None for x in 
       ['active', 'number', 'agreement_line', 'prolong', 'end_date', 
       'prolong_interval', 'prolong_unit', 'partner_id'])): 
      self.unlink_orders(fields.Date.today()) 
     return value 

    @api.model 
    def copy(self, id, default=None): 
     agreement_record = self.browse(id) 
     default.update({ 
      'state': 'empty', 
      'active': True, 
      'name': '%s*' % agreement_record['name'], 
     }) 
     return super(Agreement, self).copy(id, default=default) 

    @api.multi 
    def unlink(self): 
     for agreement in self: 
      if any(agreement.mapped('order_line')): 
       raise exceptions.Warning(
        _('You cannot remove agreements with confirmed orders!')) 
     self.unlink_orders(fields.Date.from_string(fields.Date.today())) 
     return models.Model.unlink(self) 

    @api.multi 
    def onchange_start_date(self, start_date=False): 
     if not start_date: 
      return {} 
     result = {'value': {'last_renovation_date': start_date}} 
     return result 

    @api.model 
    def revise_agreements_expirations_planned(self): 
     for agreement in self.search([('prolong', '=', 'unlimited')]): 
      if agreement.next_expiration_date <= fields.Date.today(): 
       agreement.write({'prolong': 'unlimited'}) 
     return True 

    @api.model 
    def _prepare_purchase_order_vals(self, agreement, date): 
     order_vals = { 
         'date_order': date, 
         'date_confirm': date, 
         'origin': agreement.number, 
         'partner_id': agreement.partner_id.id, 
         'state': 'draft', 
         'company_id': agreement.company_id.id, 
         'from_agreement': True, 
         'agreement_id': agreement.id, 
         'location_id': 1, 
         'fiscal_position_id': self.env['account.fiscal.position'].with_context(company_id=agreement.company_id.id).get_fiscal_position(agreement.partner_id.id), 
         'payment_term_id': agreement.partner_id.property_supplier_payment_term_id.id, 
         'currency_id': agreement.partner_id.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id, 
        } 
     order_vals['user_id'] = agreement.partner_id.user_id.id 
     return order_vals 

    @api.model 
    def _prepare_purchase_order_line_vals(self, agreement_line, order): 
     product_lang = agreement_line.product_id.with_context({ 
      'lang': order.partner_id.lang, 
      'partner_id': order.partner_id.id, 
     }) 
     fpos = order.fiscal_position_id 
     order_line_vals = { 
      'order_id': order.id, 
      'product_id': agreement_line.product_id.id, 
      'product_qty': agreement_line.quantity, 
      'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT), 
      'price_unit': 0.0, 
      'product_qty': 1.0, 
      'product_uom': agreement_line.product_id.uom_po_id.id or agreement_line.product_id.uom_id.id, 
      'name': product_lang.display_name, 
      'taxes_id': fpos.map_tax(agreement_line.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == self.company_id.id)) 
     } 
     if agreement_line.specific_price: 
      order_line_vals['price_unit'] = agreement_line.specific_price 
     order_line_vals['taxes_id'] = [(6, 0, tuple(order_line_vals['taxes_id']))] 
     if agreement_line.additional_description: 
      order_line_vals['name'] += " %s" % (
       agreement_line.additional_description) 
     return order_line_vals 

    @api.multi 
    def create_order(self, date, agreement_lines): 
     self.ensure_one() 
     order_line_obj = self.env['purchase.order.line'].with_context(
      company_id=self.company_id.id) 
     order_vals = self._prepare_purchase_order_vals(self, date) 
     order = self.env['purchase.order'].create(order_vals) 
     for agreement_line in agreement_lines: 
      order_line_vals = self._prepare_purchase_order_line_vals(
       agreement_line, order) 
      order_line_obj.create(order_line_vals) 
     agreement_lines.write({'last_order_date': fields.Date.today()}) 
     if self.state != 'orders': 
      self.state = 'orders' 
     return order 

    @api.multi 
    def _get_next_order_date(self, line, start_date): 
     self.ensure_one() 
     next_date = fields.Date.from_string(self.start_date) 
     while next_date <= start_date: 
      next_date = self.__get_next_term_date(
       next_date, line.ordering_unit, line.ordering_interval) 
     return next_date 

    @api.multi 
    def generate_agreement_orders(self, start_date, end_date): 
     self.ensure_one() 
     if not self.active: 
      return 
     lines_to_order = {} 
     exp_date = fields.Date.from_string(self.next_expiration_date) 
     if exp_date < end_date and self.prolong != 'unlimited': 
      end_date = exp_date 
     for line in self.agreement_line: 
      if not line.active_chk: 
       continue 
      next_order_date = self._get_next_order_date(line, start_date) 
      while next_order_date <= end_date: 
       if not lines_to_order.get(next_order_date): 
        lines_to_order[next_order_date] = self.env[ 
         'purchase.recurring_orders.agreement.line'] 
       lines_to_order[next_order_date] |= line 
       next_order_date = self._get_next_order_date(
        line, next_order_date) 
     dates = lines_to_order.keys() 
     dates.sort() 
     for date in dates: 
      order = self.order_line.filtered(
       lambda x: (
        fields.Date.to_string(
         fields.Datetime.from_string(x.date_order)) == 
        fields.Date.to_string(date))) 
      if not order: 
       self.create_order(
        fields.Date.to_string(date), lines_to_order[date]) 

    @api.multi 
    def generate_initial_order(self): 
     self.ensure_one() 
     agreement_lines = self.mapped('agreement_line').filtered('active_chk') 
     order = self.create_order(self.start_date, agreement_lines) 
     self.write({'state': 'first'}) 
     order.signal_workflow('order_confirm') 
     return { 
      'domain': "[('id', '=', %s)]" % order.id, 
      'view_type': 'form', 
      'view_mode': 'form', 
      'res_model': 'purchase.order', 
      'context': self.env.context, 
      'res_id': order.id, 
      'view_id': [self.env.ref('purchase.purchase_order_form').id], 
      'type': 'ir.actions.act_window', 
      'nodestroy': True 
     } 

    @api.model 
    def generate_next_orders_planned(self, years=1, start_date=None): 
     if start_date: 
      start_date = fields.Date.from_string(start_date) 
     self.search([]).generate_next_orders(
      years=years, start_date=start_date) 

    @api.multi 
    def generate_next_year_orders(self): 
     return self.generate_next_orders(years=1) 

    @api.multi 
    def generate_next_orders(self, years=1, start_date=None): 
     if not start_date: 
      start_date = fields.Date.from_string(fields.Date.today()) 
     end_date = start_date + relativedelta(years=years) 
     for agreement in self: 
      agreement.generate_agreement_orders(start_date, end_date) 
     return True 

    @api.model 
    def confirm_current_orders_planned(self): 
     tomorrow = fields.Date.to_string(
      fields.Date.from_string(fields.Date.today()) + timedelta(days=1)) 
     orders = self.env['purchase.order'].search([ 
      ('agreement_id', '!=', False), 
      ('state', 'in', ('draft', 'sent')), 
      ('date_order', '<', tomorrow) 
     ]) 
     for order in orders: 
      order.signal_workflow('order_confirm') 

    @api.multi 
    def unlink_orders(self, start_date): 
     orders = self.mapped('order_line').filtered(
      lambda x: (x.state in ('draft', 'sent') and 
         x.date_order >= start_date)) 
     orders.unlink() 


class AgreementLine(models.Model): 
    _name = 'purchase.recurring_orders.agreement.line' 

    uom_id = fields.Many2one('product_uom', string="Uom") 
    active_chk = fields.Boolean(
     string='Active', default=True, 
     help='Unchecking this field, this quota is not generated') 
    agreement_id = fields.Many2one(
     comodel_name='purchase.recurring_orders.agreement', 
     string='Agreement reference', ondelete='cascade') 
    product_id = fields.Many2one(
     comodel_name='product.product', string='Product', ondelete='set null', 
     required=True) 
    name = fields.Char(
     related="product_id.name", string='Description', store=False) 
    additional_description = fields.Char(
     string='Add. description', size=30, 
     help='Additional description that will be added to the product ' 
      'description on orders.') 
    quantity = fields.Float(
     string='Quantity', required=True, help='Quantity of the product', 
     default=1.0) 
    discount = fields.Float(string='Discount (%)', digits=(16, 2)) 
    ordering_interval = fields.Integer(
     string='Interval', required=True, default=1, 
     help="Interval in time units for making an order of this product") 
    ordering_unit = fields.Selection(
     selection=[('days', 'days'), 
        ('weeks', 'weeks'), 
        ('months', 'months'), 
        ('years', 'years')], 
     string='Interval unit', required=True, default='months') 
    last_order_date = fields.Date(
     string='Last order', help='Date of the last Purchase order generated') 
    specific_price = fields.Float(
     string='Specific price', digits_compute=dp.get_precision('Purchase Price'), 
     help='Specific price for this product. Keep empty to use the list ' 
      'price while generating order') 
    list_price = fields.Float(
     related='product_id.list_price', string="List price", readonly=True) 

    _sql_constraints = [ 
     ('line_qty_zero', 'CHECK (quantity > 0)', 
     'All product quantities must be greater than 0.\n'), 
     ('line_interval_zero', 'CHECK (ordering_interval > 0)', 
     'All ordering intervals must be greater than 0.\n'), 
    ] 

    @api.multi 
    def onchange_product_id(self, product_id=False): 
     result = {} 
     if product_id: 
      product = self.env['product.product'].browse(product_id) 
      if product: 
       result['value'] = {'name': product['name']} 
     return result 


class AgreementRenewal(models.Model): 
    _name = 'purchase.recurring_orders.agreement.renewal' 

    agreement_id = fields.Many2one(
     comodel_name='purchase.recurring_orders.agreement', 
     string='Agreement reference', ondelete='cascade', select=True) 
    date = fields.Date(string='Date', help="Date of the renewal") 
    comments = fields.Char(
     string='Comments', size=200, help='Renewal comments') 

我發現這個職位可能有關針對此問題: https://github.com/adhoc-dev/odoo-addons/issues/113

我一直懷疑這條線:

'taxes_id': fpos.map_tax(agreement_line.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == self.company_id.id)) 

可能所以我用這個問題打印說明:

print fpos.map_tax(agreement_line.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == self.company_id.id)) 
account.tax(6,) 

但我不知道如何繼續下去。

任何幫助/提示問題可能會在哪裏歡迎。

回答

4

下面的代碼

fpos.map_tax(agreement_line.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == self.company_id.id)) 

返回記錄,但數據庫未存儲記錄集。這就是爲什麼它與信息失敗。

ProgrammingError: can't adapt type 'account.tax'

解決方案

recordset.id // for singleton recordset 
result : id (integer) 

recordset.ids // for multi recordset 
result : list of ids [] 

現在它取決於你打算寫這個值類型的字段。如果它是一個Many2one字段,那麼它應該是一個整數。你可以用得到recordset.id

注1 -

注意記錄能的記錄,這意味着複式記錄,這將看起來像account.tax(6,7,12,34)。在這種情況下,如果你說recordset.id,它會失敗,因爲有多個ID,並且不確定你想要哪一個。因此,要從記錄集中獲取ID,您可以執行recordset.id,如果空白記錄集會導致列表類似[6,7,12,34]或[]。

NOTE2 -

如果你試圖在Many2many外地來寫。你可以做類似

self.taxes_id = [(6,0, recordset.ids)] 
+0

只需在記錄集中添加.ids模塊就可以正常工作。 'taxes_id':fpos.map_tax(agreement_line.product_id.supplier_taxes_id.filtered(lambda r:r.company_id.id == self.company_id.id)).ids –