2
votes

I want to add my created record to one2many field. However it results ValueError: Expected singleton: product.job_line(762, 763).

I don't know why it doesn't separate into two object but it merge into one object when JobContainer call the write method. After that it results the error as I told you before.

How to solve this one ?

Here is my code of SaleOrder :

# -*- coding: utf-8 -*-
import logging
from odoo import models, fields, api

_logger = logging.getLogger(__name__)

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    job_container = fields.One2many('job.container', 'order')
    discount = fields.Float()
    state = fields.Selection(
        [
            ('draft', 'Quotation'),
            ('sent', 'Quotation Sent'),
            ('pending', 'Pending Approval'),
            ('sale', 'Sales Order'),
            ('done', 'Locked'),
            ('cancel', 'Cancelled'),
        ],
        string='Status', readonly=True, copy=False, index=True,
        track_visibility='onchange', default='draft'
    )

    @api.onchange('discount')
    def _amount_all(self):
        super(SaleOrder, self)._amount_all()
        for order in self:
            total = order.amount_total
            if order.discount > 1:
                total -= order.discount
            else:
                total -= total * order.discount
            order.update({
                'amount_total': total
            })

    @api.multi
    def action_confirm(self):
        if self.state != 'pending' and self.get_full_price() != self.amount_total:
            self.state = 'pending'
        elif self.job_container:
            self.state = 'sale'
            self.confirmation_date = fields.Datetime.now()
            self.job_container.confirmation_date = self.confirmation_date
        else:
            self.create_job_container()
            self.delegate_job_values()
            super(SaleOrder, self).action_confirm()

    def delegate_job_values(self):
        total_price = self.amount_total
        price_list = []
        for line in self.order_line:
            qty = line.product_uom_qty
            product = line.product_id.product_tmpl_id
            price = line.price_total*self.get_discount_percent()/qty
            if price == 0:
                price = line.price_total/qty
            for _ in range(0, int(qty)):
                price_list += product.create_price_list(price)
        for index in range(0, len(self.job_container.job_lines)):
            total_price -= price_list[index]
            if index == len(self.job_container.job_lines)-1:
            self.job_container.job_lines[index].update({
                    'cost': price_list[index] + total_price,
                    'order':self.id,
                })
            else:
                self.job_container.job_lines[index].update({
                    'cost': price_list[index],
                    'order': self.id,
                })

    def get_full_price(self):
        total_price = 0
        for line in self.order_line:
            total_price += line.product_id.product_tmpl_id.list_price*line.product_uom_qty
        return total_price

    def create_job_container(self):
        container = self.env['job.container'].create({
            'order': self.id,
            'partner': self.partner_id.id,
            'confirmation_date': fields.Datetime.now()
        })
        for line in self.order_line:
            qty = line.product_uom_qty
            product = line.product_id.product_tmpl_id
            job_lines = []
            for _ in range(0, int(qty)):
                for job_line in product.create_job_lines():
                    _logger.info("JL : "+str(job_line))
                    job_lines.append(job_line.id)
                    # container.add_job_line(job_line)
                    # container.update({
                    #     'job_lines': [(1,job_line.id,job_line)]
                    # })
                container.job_lines += job_line
        _logger.info('Job Line '+str(job_lines))
        # container.job_lines = job_lines
        container.update_job_lines_left()

    @api.one
    def action_approve(self):
        self.action_confirm()

    def write(self, val):
        old_state = self.state
        _logger.info('Value : '+str(val))
        _logger.info('Old State '+old_state)
        super(SaleOrder, self).write(val)
        if old_state == 'sale' and 'confirmation_date' not in val:
            self.state = 'pending'
        return True

    def get_discount_percent(self):
        if self.discount < 1:
            return self.discount
        else:
            return self.discount/(self.amount_total+self.discount)

    def update_invoice_order(self):
        for invoice in self.invoice_ids:
            invoice.update({'order':self.id})

    @api.multi
    def action_invoice_create(self, grouped=False, final=False):
        super(SaleOrder, self).action_invoice_create(grouped, final)
        self.update_invoice_order()

    @api.one
    def pending(self):
        self.state = 'pending'
        self.job_container.unpending()

Here is my code of Job Container :

# -*- coding: utf-8 -*-
from odoo import models, fields, api
import logging
_logger = logging.getLogger(__name__)

class JobContainer(models.Model):
    _name = 'job.container'
    _order = 'jobs_left'
    _inherit = ['mail.thread']

    order = fields.Many2one('sale.order', readonly=True)
    partner = fields.Many2one('res.partner', readonly=True)
    confirmation_date = fields.Datetime(readonly=True, track_visibility='onchange')
    job_lines = fields.One2many('product.job_line', 'container')
    jobs_left = fields.Integer(compute='update_job_lines_left')
    to_pending = fields.Boolean()

    def pending(self):
        self.order.pending()

    def unpending(self):
        self.to_pending = False
        for line in self.job_lines:
            line.to_pending = False

    @api.one
    def update_job_lines_left(self):
        left = 0
        for job_line in self.job_lines:
            if not job_line.is_finish and not job_line.state=='cancel':
                left += 1
        self.jobs_left = left

    def adjust_cost(self,cost):
        sum = 0
        for line in self.job_lines:
            if line.state=='active' and not line.is_finish:
                line.cost+=round(cost/len(self.job_lines),2)
                sum += line.cost
        left = self.order.amount_total-sum
        for line in self.job_lines:
            if line.state=='active' and not line.is_finish:
                line.cost+=left
                break

    @api.onchange('job_lines')
    def notify_pending(self):
        _logger.info("IN")
        self.to_pending = True

    @api.multi
    def write(self,val):
        _logger.info(val)
        super(JobContainer,self).write(val)
        _logger.info(str(val))
        if self.to_pending:
            _logger.info("INNNN")
            self.pending()
        return True
1

1 Answers

4
votes

Take a look at this answer: https://stackoverflow.com/a/26012940/7990703

(0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
(1, ID, { values })    update the linked record with id = ID (write *values* on it)
(2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)

Example:
[(0, 0, {'field_name':field_value_record1, ...}), (0, 0, {'field_name':field_value_record2, ...})]