Exemplo n.º 1
0
class WebsiteResPartner(osv.Model):
    _name = 'res.partner'
    _inherit = [
        'res.partner', 'website.seo.metadata', 'website.published.mixin'
    ]

    def _get_ids(self, cr, uid, ids, flds, args, context=None):
        return {i: i for i in ids}

    def _set_private(self, cr, uid, ids, field_name, value, arg, context=None):
        return self.write(cr,
                          uid,
                          ids, {'website_published': not value},
                          context=context)

    def _get_private(self, cr, uid, ids, field_name, arg, context=None):
        return dict((rec.id, not rec.website_published)
                    for rec in self.browse(cr, uid, ids, context=context))

    def _search_private(self, cr, uid, obj, name, args, context=None):
        return [('website_published', '=', not args[0][2])]

    _columns = {
        'website_private':
        fields.function(_get_private,
                        fnct_inv=_set_private,
                        fnct_search=_search_private,
                        type='boolean',
                        string='Private Profile'),
        'website_description':
        fields.html('Website Partner Full Description', strip_style=True),
        'website_short_description':
        fields.text('Website Partner Short Description'),
        # hack to allow using plain browse record in qweb views
        'self':
        fields.function(_get_ids, type='many2one', relation=_name),
    }

    def _website_url(self, cr, uid, ids, field_name, arg, context=None):
        res = super(WebsiteResPartner, self)._website_url(cr,
                                                          uid,
                                                          ids,
                                                          field_name,
                                                          arg,
                                                          context=context)
        for partner in self.browse(cr, uid, ids, context=context):
            res[partner.id] = "/partners/%s" % slug(partner)
        return res

    _defaults = {
        'website_private': True,
    }
Exemplo n.º 2
0
class res_partner(osv.osv):
    _name = 'res.partner'
    _inherit = 'res.partner'

    def _compute_payment_method_count(self,
                                      cr,
                                      uid,
                                      ids,
                                      field_names,
                                      arg,
                                      context=None):
        result = {}
        payment_data = self.pool['payment.method'].read_group(
            cr,
            uid, [('partner_id', 'in', ids)], ['partner_id'], ['partner_id'],
            context=context)
        mapped_data = dict([(payment['partner_id'][0],
                             payment['partner_id_count'])
                            for payment in payment_data])
        for partner in self.browse(cr, uid, ids, context=context):
            result[partner.id] = mapped_data.get(partner.id, 0)
        return result

    _columns = {
        'payment_method_ids':
        fields.one2many('payment.method', 'partner_id', 'Payment Methods'),
        'payment_method_count':
        fields.function(_compute_payment_method_count,
                        string='Count Payment Method',
                        type="integer"),
    }
Exemplo n.º 3
0
class TestFunctionNoInfiniteRecursion(osv.Model):
    _name = 'test_old_api.function_noinfiniterecursion'

    def _compute_f1(self, cr, uid, ids, fname, arg, context=None):
        res = {}
        for tf in self.browse(cr, uid, ids, context=context):
            res[tf.id] = 'create' in tf.f0 and 'create' or 'write'
        cntobj = self.pool['test_old_api.function_counter']
        cnt_id = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'test_new_api.c1')
        cntobj.write(cr,
                     uid,
                     cnt_id, {'access': datetime.datetime.now()},
                     context=context)
        return res

    _columns = {
        'f0':
        fields.char('Char Field'),
        'f1':
        fields.function(_compute_f1,
                        type='char',
                        string='Function Field',
                        store=True),
    }
Exemplo n.º 4
0
class product_product(osv.osv):
    _inherit = "product.product"
    def _bom_orders_count(self, cr, uid, ids, field_name, arg, context=None):
        Production = self.pool('mrp.production')
        res = {}
        for product_id in ids:
            res[product_id] = Production.search_count(cr,uid, [('product_id', '=', product_id)], context=context)
        return res

    _columns = {
        'mo_count': fields.function(_bom_orders_count, string='# Manufacturing Orders', type='integer'),
    }

    def action_view_bom(self, cr, uid, ids, context=None):
        result = self.pool.get("product.template")._get_act_window_dict(cr, uid, 'mrp.product_open_bom', context=context)
        templates = [product.product_tmpl_id.id for product in self.browse(cr, uid, ids, context=context)]
        # bom specific to this variant or global to template
        context = {
            'search_default_product_tmpl_id': templates[0],
            'search_default_product_id': ids[0],
            'default_product_tmpl_id': templates[0],
            'default_product_id': ids[0],
        }
        result['context'] = str(context)
        return result
Exemplo n.º 5
0
class project_issue(osv.osv):
    _inherit = 'project.issue'
    _description = 'project issue'

    def _hours_get(self, cr, uid, ids, field_names, args, context=None):
        res = {}
        for issue in self.browse(cr, uid, ids, context=context):
            res[issue.id] = {'progress': issue.task_id.progress or 0.0}
        return res

    def _get_issue_task(self, cr, uid, task_ids, context=None):
        return self.pool['project.issue'].search(cr,
                                                 uid,
                                                 [('task_id', 'in', task_ids)],
                                                 context=context)

    _columns = {
        'progress':
        fields.function(_hours_get,
                        string='Progress (%)',
                        multi='line_id',
                        group_operator="avg",
                        help="Computed as: Time Spent / Total Time.",
                        store={
                            'project.issue':
                            (lambda self, cr, uid, ids, c={}: ids, ['task_id'],
                             10),
                            'project.task':
                            (_get_issue_task, ['progress'], 10),
                        }),
        'timesheet_ids':
        fields.one2many('account.analytic.line', 'issue_id', 'Timesheets'),
        'analytic_account_id':
        fields.many2one('account.analytic.account', 'Analytic Account'),
    }

    def on_change_project(self, cr, uid, ids, project_id, context=None):
        if not project_id:
            return {'value': {'analytic_account_id': False}}

        result = super(project_issue, self).on_change_project(cr,
                                                              uid,
                                                              ids,
                                                              project_id,
                                                              context=context)

        project = self.pool.get('project.project').browse(cr,
                                                          uid,
                                                          project_id,
                                                          context=context)
        if 'value' not in result:
            result['value'] = {}

        account = project.analytic_account_id
        if account:
            result['value']['analytic_account_id'] = account.id

        return result
Exemplo n.º 6
0
class MassMailingList(osv.Model):
    """Model of a contact list. """
    _name = 'mail.mass_mailing.list'
    _order = 'name'
    _description = 'Mailing List'

    def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
        result = dict.fromkeys(ids, 0)
        Contacts = self.pool.get('mail.mass_mailing.contact')
        for group in Contacts.read_group(cr,
                                         uid, [('list_id', 'in', ids),
                                               ('opt_out', '!=', True)],
                                         ['list_id'], ['list_id'],
                                         context=context):
            result[group['list_id'][0]] = group['list_id_count']
        return result

    _columns = {
        'name':
        fields.char('Mailing List', required=True),
        'active':
        fields.boolean('Active'),
        'create_date':
        fields.datetime('Creation Date'),
        'contact_nbr':
        fields.function(
            _get_contact_nbr,
            type='integer',
            string='Number of Contacts',
        ),
        'popup_content':
        fields.html("Website Popup Content",
                    translate=True,
                    required=True,
                    sanitize=False),
        'popup_redirect_url':
        fields.char("Website Popup Redirect URL"),
    }

    def _get_default_popup_content(self, cr, uid, context=None):
        return """<div class="modal-header text-center">
    <h3 class="modal-title mt8">YuanCloud Presents</h3>
</div>
<div class="o_popup_message">
    <font>7</font>
    <strong>Business Hacks</strong>
    <span> to<br/>boost your marketing</span>
</div>
<p class="o_message_paragraph">Join our Marketing newsletter and get <strong>this white paper instantly</strong></p>"""

    _defaults = {
        'active': True,
        'popup_content': _get_default_popup_content,
        'popup_redirect_url': '/',
    }
Exemplo n.º 7
0
class ResCompany(osv.Model):
    _inherit = "res.company"

    def _get_paypal_account(self, cr, uid, ids, name, arg, context=None):
        Acquirer = self.pool['payment.acquirer']
        company_id = self.pool['res.users'].browse(
            cr, uid, uid, context=context).company_id.id
        paypal_ids = Acquirer.search(cr,
                                     uid, [
                                         ('website_published', '=', True),
                                         ('name', 'ilike', 'paypal'),
                                         ('company_id', '=', company_id),
                                     ],
                                     limit=1,
                                     context=context)
        if paypal_ids:
            paypal = Acquirer.browse(cr, uid, paypal_ids[0], context=context)
            return dict.fromkeys(ids, paypal.paypal_email_account)
        return dict.fromkeys(ids, False)

    def _set_paypal_account(self, cr, uid, id, name, value, arg, context=None):
        Acquirer = self.pool['payment.acquirer']
        company_id = self.pool['res.users'].browse(
            cr, uid, uid, context=context).company_id.id
        paypal_account = self.browse(cr, uid, id,
                                     context=context).paypal_account
        paypal_ids = Acquirer.search(
            cr,
            uid, [
                ('website_published', '=', True),
                ('paypal_email_account', '=', paypal_account),
                ('company_id', '=', company_id),
            ],
            context=context)
        if paypal_ids:
            Acquirer.write(cr,
                           uid,
                           paypal_ids, {'paypal_email_account': value},
                           context=context)
        return True

    _columns = {
        'paypal_account':
        fields.function(
            _get_paypal_account,
            fnct_inv=_set_paypal_account,
            nodrop=True,
            type='char',
            string='Paypal Account',
            help=
            "Paypal username (usually email) for receiving online payments."),
    }
Exemplo n.º 8
0
class product_template(osv.osv):
    _inherit = "product.template"
    def _bom_orders_count(self, cr, uid, ids, field_name, arg, context=None):
        Bom = self.pool('mrp.bom')
        res = {}
        for product_tmpl_id in ids:
            nb = Bom.search_count(cr, uid, [('product_tmpl_id', '=', product_tmpl_id)], context=context)
            res[product_tmpl_id] = {
                'bom_count': nb,
            }
        return res

    def _bom_orders_count_mo(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for product_tmpl_id in self.browse(cr, uid, ids):
            res[product_tmpl_id.id] = sum([p.mo_count for p in product_tmpl_id.product_variant_ids])
        return res

    _columns = {
        'bom_ids': fields.one2many('mrp.bom', 'product_tmpl_id','Bill of Materials'),
        'bom_count': fields.function(_bom_orders_count, string='# Bill of Material', type='integer', multi="_bom_order_count"),
        'mo_count': fields.function(_bom_orders_count_mo, string='# Manufacturing Orders', type='integer'),
        'produce_delay': fields.float('Manufacturing Lead Time', help="Average delay in days to produce this product. In the case of multi-level BOM, the manufacturing lead times of the components will be added."),
    }

    _defaults = {
        'produce_delay': 1,
    }
    
    
    def action_view_mos(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr, uid, 'mrp.act_product_mrp_production', context=context)
        if len(ids) == 1 and len(products) == 1:
            result['context'] = "{'default_product_id': " + str(products[0]) + ", 'search_default_product_id': " + str(products[0]) + "}"
        else:
            result['domain'] = "[('product_id','in',[" + ','.join(map(str, products)) + "])]"
            result['context'] = "{}"
        return result
Exemplo n.º 9
0
class res_partner(osv.osv):
    def _task_count(self, cr, uid, ids, field_name, arg, context=None):
        Task = self.pool['project.task']
        return {
            partner_id: Task.search_count(cr,uid, [('partner_id', '=', partner_id)], context=context)
            for partner_id in ids
        }
    
    """ Inherits partner and adds Tasks information in the partner form """
    _inherit = 'res.partner'
    _columns = {
        'task_ids': fields.one2many('project.task', 'partner_id', 'Tasks'),
        'task_count': fields.function(_task_count, string='# Tasks', type='integer'),
    }
Exemplo n.º 10
0
class hr_employee(osv.osv):
    _name = "hr.employee"
    _description = "Employee"
    _inherit = "hr.employee"

    def attachment_tree_view(self, cr, uid, ids, context):
        domain = [
            '&', ('res_model', '=', 'hr.employee'), ('res_id', 'in', ids)
        ]
        res_id = ids and ids[0] or False
        return {
            'name':
            _('Attachments'),
            'domain':
            domain,
            'res_model':
            'ir.attachment',
            'type':
            'ir.actions.act_window',
            'view_id':
            False,
            'view_mode':
            'kanban,tree,form',
            'view_type':
            'form',
            'limit':
            80,
            'context':
            "{'default_res_model': '%s','default_res_id': %d}" %
            (self._name, res_id)
        }

    def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
        res = {}
        attachment = self.pool.get('ir.attachment')
        for id in ids:
            employee_attachments = attachment.search(
                cr,
                uid, [('res_model', '=', 'hr.employee'), ('res_id', '=', id)],
                context=context,
                count=True)
            res[id] = employee_attachments or 0
        return res

    _columns = {
        'doc_count':
        fields.function(_get_attached_docs,
                        string="Number of documents attached",
                        type='integer')
    }
Exemplo n.º 11
0
class res_partner(osv.osv):
    def _issue_count(self, cr, uid, ids, field_name, arg, context=None):
        Issue = self.pool['project.issue']
        return {
            partner_id: Issue.search_count(cr, uid,
                                           [('partner_id', '=', partner_id)])
            for partner_id in ids
        }

    """ Inherits partner and adds Issue information in the partner form """
    _inherit = 'res.partner'
    _columns = {
        'issue_count':
        fields.function(_issue_count, string='# Issues', type='integer'),
    }
Exemplo n.º 12
0
class TestFunctionCounter(osv.Model):
    _name = 'test_old_api.function_counter'

    def _compute_cnt(self, cr, uid, ids, fname, arg, context=None):
        res = {}
        for cnt in self.browse(cr, uid, ids, context=context):
            res[cnt.id] = cnt.access and cnt.cnt + 1 or 0
        return res

    _columns = {
        'access':
        fields.datetime('Datetime Field'),
        'cnt':
        fields.function(_compute_cnt,
                        type='integer',
                        string='Function Field',
                        store=True),
    }
Exemplo n.º 13
0
class hr_employee(osv.osv):
    _name = "hr.employee"
    _description = "Employee"
    _inherit = "hr.employee"

    def _trainings_count(self, cr, uid, ids, field_name, arg, context=None):
        Training = self.pool['hr.training.record']
        sql_str = '''select rel.*,emp.* from training_employee_rel as rel join hr_employee as emp on rel.employee_id = emp.id
                              where emp.id=%s
                              ''' % (ids[0])
        # _logger.info('根据培训课程ID,查找讲师=%s' % sql_str)
        self._cr.execute(sql_str)
        res = self._cr.fetchall()
        return {ids[0]:len(res)}

    _columns = {
        'trainings_count': fields.function(_trainings_count, type='integer', string='Trainings'),
    }
Exemplo n.º 14
0
class ir_attachment(osv.osv):

    _inherit = "ir.attachment"

    def _local_url_get(self, cr, uid, ids, name, arg, context=None):
        result = {}
        for attach in self.browse(cr, uid, ids, context=context):
            if attach.url:
                result[attach.id] = attach.url
            else:
                result[attach.id] = '/web/image/%s?unique=%s' % (
                    attach.id, attach.checksum)
        return result

    _columns = {
        'local_url':
        fields.function(_local_url_get, string="Attachment URL", type='char'),
    }
Exemplo n.º 15
0
class product_product(Model):
    _inherit = 'product.product'

    def _get_supplier_goodies_ids(self, cr, uid, ids, name, arg, context=None):
        if context is None: context = {}
        if context.get('date'):
            date = context['date']
        else:
            date = datetime.today().strftime('%Y-%m-%d')
        res = {}
        link_obj = self.pool.get('product.link')
        for product_id in ids:
            res[product_id] = link_obj.search(
                cr,
                uid, [
                    ['product_id', '=', ids[0]],
                    ['type', '=', 'goodies'],
                    '|',
                    ['start_date', '<=', date],
                    ['start_date', '=', False],
                    '|',
                    ['end_date', '>=', date],
                    ['end_date', '=', False],
                    ['supplier_goodies', '=', True],
                ],
                context=context)
        return res

    _columns = {
        'supplier_goodies_ids':
        fields.function(_get_supplier_goodies_ids,
                        type='many2many',
                        relation="product.link"),
    }

    def is_purchase_goodies(self, cr, uid, ids, context=None):
        return self.pool.get('product.link').search(
            cr,
            uid, [
                ['linked_product_id', '=', ids[0]],
                ['type', '=', 'goodies'],
                ['supplier_goodies', '=', True],
            ],
            context=context) and True or False
Exemplo n.º 16
0
class project(osv.Model):
    _inherit = "project.project"

    def _get_alias_models(self, cr, uid, context=None):
        res = super(project, self)._get_alias_models(cr, uid, context=context)
        res.append(("project.issue", "Issues"))
        return res

    def _issue_count(self, cr, uid, ids, field_name, arg, context=None):
        Issue = self.pool['project.issue']
        return {
            project_id: Issue.search_count(cr,
                                           uid,
                                           [('project_id', '=', project_id),
                                            ('stage_id.fold', '=', False)],
                                           context=context)
            for project_id in ids
        }

    _columns = {
        'issue_count':
        fields.function(
            _issue_count,
            type='integer',
            string="Issues",
        ),
        'issue_ids':
        fields.one2many('project.issue',
                        'project_id',
                        string="Issues",
                        domain=[('stage_id.fold', '=', False)]),
    }

    @api.multi
    def write(self, vals):
        res = super(project, self).write(vals)
        if 'active' in vals:
            # archiving/unarchiving a project does it on its issues, too
            issues = self.with_context(active_test=False).mapped('issue_ids')
            issues.write({'active': vals['active']})
        return res
Exemplo n.º 17
0
class website_pricelist(osv.Model):
    _name = 'website_pricelist'
    _description = 'Website Pricelist'

    def _get_display_name(self, cr, uid, ids, name, arg, context=None):
        result = {}
        for o in self.browse(cr, uid, ids, context=context):
            result[o.id] = _("Website Pricelist for %s") % o.pricelist_id.name
        return result

    _columns = {
        'name': fields.function(_get_display_name, string='Pricelist Name', type="char"),
        'website_id': fields.many2one('website', string="Website", required=True),
        'selectable': fields.boolean('Selectable', help="Allow the end user to choose this price list"),
        'pricelist_id': fields.many2one('product.pricelist', string='Pricelist'),
        'country_group_ids': fields.many2many('res.country.group', 'res_country_group_website_pricelist_rel',
                                              'website_pricelist_id', 'res_country_group_id', string='Country Groups'),
    }

    def clear_cache(self):
        # website._get_pl() is cached to avoid to recompute at each request the
        # list of available pricelists. So, we need to invalidate the cache when
        # we change the config of website price list to force to recompute.
        website = self.pool['website']
        website._get_pl.clear_cache(website)

    def create(self, cr, uid, data, context=None):
        res = super(website_pricelist, self).create(cr, uid, data, context=context)
        self.clear_cache()
        return res

    def write(self, cr, uid, ids, data, context=None):
        res = super(website_pricelist, self).write(cr, uid, ids, data, context=context)
        self.clear_cache()
        return res

    def unlink(self, cr, uid, ids, context=None):
        res = super(website_pricelist, self).unlink(cr, uid, ids, context=context)
        self.clear_cache()
        return res
Exemplo n.º 18
0
class res_partner(osv.osv):
    _inherit = 'res.partner'

    def _sale_order_count(self, cr, uid, ids, field_name, arg, context=None):
        res = dict(map(lambda x: (x, 0), ids))
        # The current user may not have access rights for sale orders
        try:
            for partner in self.browse(cr, uid, ids, context):
                res[partner.id] = len(partner.sale_order_ids) + len(
                    partner.mapped('child_ids.sale_order_ids'))
        except:
            pass
        return res

    _columns = {
        'sale_order_count':
        fields.function(_sale_order_count,
                        string='# of Sales Order',
                        type='integer'),
        'sale_order_ids':
        fields.one2many('sale.order', 'partner_id', 'Sales Order')
    }
Exemplo n.º 19
0
class hr_employee(osv.osv):
    '''
    Employee
    '''

    _inherit = 'hr.employee'
    _description = 'Employee'

    def _timesheet_count(self, cr, uid, ids, field_name, arg, context=None):
        Sheet = self.pool['hr_timesheet_sheet.sheet']
        return {
            employee_id:
            Sheet.search_count(cr,
                               uid, [('employee_id', '=', employee_id)],
                               context=context)
            for employee_id in ids
        }

    _columns = {
        'timesheet_count':
        fields.function(_timesheet_count, type='integer', string='Timesheets'),
    }
Exemplo n.º 20
0
class mrp_repair_fee(osv.osv, ProductChangeMixin):
    _name = 'mrp.repair.fee'
    _description = 'Repair Fees Line'

    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        tax_obj = self.pool.get('account.tax')
        cur_obj = self.pool.get('res.currency')
        for line in self.browse(cr, uid, ids, context=context):
            if line.to_invoice:
                cur = line.repair_id.pricelist_id.currency_id
                taxes = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur.id, line.product_uom_qty, line.product_id.id, line.repair_id.partner_id.id)
                res[line.id] = taxes['total_included']
            else:
                res[line.id] = 0
        return res

    _columns = {
        'repair_id': fields.many2one('mrp.repair', 'Repair Order Reference', required=True, ondelete='cascade', select=True),
        'name': fields.char('Description', select=True, required=True),
        'product_id': fields.many2one('product.product', 'Product'),
        'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
        'price_unit': fields.float('Unit Price', required=True),
        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
        'price_subtotal': fields.function(_amount_line, string='Subtotal', digits=0),
        'tax_id': fields.many2many('account.tax', 'repair_fee_line_tax', 'repair_fee_line_id', 'tax_id', 'Taxes'),
        'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True, copy=False),
        'to_invoice': fields.boolean('To Invoice'),
        'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
    }

    _defaults = {
        'to_invoice': lambda *a: True,
    }
Exemplo n.º 21
0
class product_category(osv.osv):
    _inherit = 'product.category'

    def calculate_total_routes(self, cr, uid, ids, name, args, context=None):
        res = {}
        for categ in self.browse(cr, uid, ids, context=context):
            categ2 = categ
            routes = [x.id for x in categ.route_ids]
            while categ2.parent_id:
                categ2 = categ2.parent_id
                routes += [x.id for x in categ2.route_ids]
            res[categ.id] = routes
        return res

    _columns = {
        'route_ids':
        fields.many2many('stock.location.route',
                         'stock_location_route_categ',
                         'categ_id',
                         'route_id',
                         'Routes',
                         domain="[('product_categ_selectable', '=', True)]"),
        'removal_strategy_id':
        fields.many2one(
            'product.removal',
            'Force Removal Strategy',
            help=
            "Set a specific removal strategy that will be used regardless of the source location for this product category"
        ),
        'total_route_ids':
        fields.function(calculate_total_routes,
                        relation='stock.location.route',
                        type='many2many',
                        string='Total routes',
                        readonly=True),
    }
Exemplo n.º 22
0
class BlogPost(osv.Model):
    _name = "blog.post"
    _description = "Blog Post"
    _inherit = ['mail.thread', 'website.seo.metadata', 'website.published.mixin']
    _order = 'id DESC'
    _mail_post_access = 'read'

    def _website_url(self, cr, uid, ids, field_name, arg, context=None):
        res = super(BlogPost, self)._website_url(cr, uid, ids, field_name, arg, context=context)
        for blog_post in self.browse(cr, uid, ids, context=context):
            res[blog_post.id] = "/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post))
        return res

    def _compute_ranking(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for blog_post in self.browse(cr, uid, ids, context=context):
            age = datetime.now() - datetime.strptime(blog_post.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
            res[blog_post.id] = blog_post.visits * (0.5+random.random()) / max(3, age.days)
        return res

    def _default_content(self, cr, uid, context=None):
        return '''  <div class="container">
                        <section class="mt16 mb16">
                            <p class="o_default_snippet_text">''' + _("Start writing here...") + '''</p>
                        </section>
                    </div> '''

    _columns = {
        'name': fields.char('Title', required=True, translate=True),
        'subtitle': fields.char('Sub Title', translate=True),
        'author_id': fields.many2one('res.partner', 'Author'),
        'cover_properties': fields.text('Cover Properties'),
        'blog_id': fields.many2one(
            'blog.blog', 'Blog',
            required=True, ondelete='cascade',
        ),
        'tag_ids': fields.many2many(
            'blog.tag', string='Tags',
        ),
        'content': fields.html('Content', translate=True, sanitize=False),
        'website_message_ids': fields.one2many(
            'mail.message', 'res_id',
            domain=lambda self: [
                '&', '&', ('model', '=', self._name), ('message_type', '=', 'comment'), ('path', '=', False)
            ],
            string='Website Messages',
            help="Website communication history",
        ),
        # creation / update stuff
        'create_date': fields.datetime(
            'Created on',
            select=True, readonly=True,
        ),
        'create_uid': fields.many2one(
            'res.users', 'Author',
            select=True, readonly=True,
        ),
        'write_date': fields.datetime(
            'Last Modified on',
            select=True, readonly=True,
        ),
        'write_uid': fields.many2one(
            'res.users', 'Last Contributor',
            select=True, readonly=True,
        ),
        'author_avatar': fields.related(
            'author_id', 'image_small',
            string="Avatar", type="binary"),
        'visits': fields.integer('No of Views'),
        'ranking': fields.function(_compute_ranking, string='Ranking', type='float'),
    }

    _defaults = {
        'name': '',
        'content': _default_content,
        'cover_properties': '{"background-image": "none", "background-color": "oe_none", "opacity": "0.6", "resize_class": ""}',
        'author_id': lambda self, cr, uid, ctx=None: self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id,
    }

    def html_tag_nodes(self, html, attribute=None, tags=None, context=None):
        """ Processing of html content to tag paragraphs and set them an unique
        ID.
        :return result: (html, mappin), where html is the updated html with ID
                        and mapping is a list of (old_ID, new_ID), where old_ID
                        is None is the paragraph is a new one. """

        existing_attributes = []
        mapping = []
        if not html:
            return html, mapping
        if tags is None:
            tags = ['p']
        if attribute is None:
            attribute = 'data-unique-id'

        # form a tree
        root = lxml.html.fragment_fromstring(html, create_parent='div')
        if not len(root) and root.text is None and root.tail is None:
            return html, mapping

        # check all nodes, replace :
        # - img src -> check URL
        # - a href -> check URL
        for node in root.iter():
            if node.tag not in tags:
                continue
            ancestor_tags = [parent.tag for parent in node.iterancestors()]

            old_attribute = node.get(attribute)
            new_attribute = old_attribute
            if not new_attribute or (old_attribute in existing_attributes):
                if ancestor_tags:
                    ancestor_tags.pop()
                counter = random.randint(10000, 99999)
                ancestor_tags.append('counter_%s' % counter)
                new_attribute = '/'.join(reversed(ancestor_tags))
                node.set(attribute, new_attribute)

            existing_attributes.append(new_attribute)
            mapping.append((old_attribute, new_attribute))

        html = lxml.html.tostring(root, pretty_print=False, method='html')
        # this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that
        if html.startswith('<div>') and html.endswith('</div>'):
            html = html[5:-6]
        return html, mapping

    def _postproces_content(self, cr, uid, id, content=None, context=None):
        if content is None:
            content = self.browse(cr, uid, id, context=context).content
        if content is False:
            return content

        content, mapping = self.html_tag_nodes(content, attribute='data-chatter-id', tags=['p'], context=context)
        if id:  # not creating
            existing = [x[0] for x in mapping if x[0]]
            msg_ids = self.pool['mail.message'].search(cr, SUPERUSER_ID, [
                ('res_id', '=', id),
                ('model', '=', self._name),
                ('path', 'not in', existing),
                ('path', '!=', False)
            ], context=context)
            self.pool['mail.message'].unlink(cr, SUPERUSER_ID, msg_ids, context=context)

        return content

    def _check_for_publication(self, cr, uid, ids, vals, context=None):
        if vals.get('website_published'):
            base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
            for post in self.browse(cr, uid, ids, context=context):
                post.blog_id.message_post(
                    body='<p>%(post_publication)s <a href="%(base_url)s/blog/%(blog_slug)s/post/%(post_slug)s">%(post_link)s</a></p>' % {
                        'post_publication': _('A new post %s has been published on the %s blog.') % (post.name, post.blog_id.name),
                        'post_link': _('Click here to access the post.'),
                        'base_url': base_url,
                        'blog_slug': slug(post.blog_id),
                        'post_slug': slug(post),
                    },
                    subtype='website_blog.mt_blog_blog_published')
            return True
        return False

    def create(self, cr, uid, vals, context=None):
        if context is None:
            context = {}
        if 'content' in vals:
            vals['content'] = self._postproces_content(cr, uid, None, vals['content'], context=context)
        create_context = dict(context, mail_create_nolog=True)
        post_id = super(BlogPost, self).create(cr, uid, vals, context=create_context)
        self._check_for_publication(cr, uid, [post_id], vals, context=context)
        return post_id

    def write(self, cr, uid, ids, vals, context=None):
        if isinstance(ids, (int, long)):
            ids = [ids]
        if 'content' in vals:
            vals['content'] = self._postproces_content(cr, uid, ids[0], vals['content'], context=context)
        result = super(BlogPost, self).write(cr, uid, ids, vals, context)
        self._check_for_publication(cr, uid, ids, vals, context=context)
        return result

    def get_access_action(self, cr, uid, ids, context=None):
        """ Override method that generated the link to access the document. Instead
        of the classic form view, redirect to the post on the website directly """
        post = self.browse(cr, uid, ids[0], context=context)
        return {
            'type': 'ir.actions.act_url',
            'url': '/blog/%s/post/%s' % (post.blog_id.id, post.id),
            'target': 'self',
            'res_id': self.id,
        }

    def _notification_get_recipient_groups(self, cr, uid, ids, message, recipients, context=None):
        """ Override to set the access button: everyone can see an access button
        on their notification email. It will lead on the website view of the
        post. """
        res = super(BlogPost, self)._notification_get_recipient_groups(cr, uid, ids, message, recipients, context=context)
        access_action = self._notification_link_helper('view', model=message.model, res_id=message.res_id)
        for category, data in res.iteritems():
            res[category]['button_access'] = {'url': access_action, 'title': _('View Blog Post')}
        return res
Exemplo n.º 23
0
class mrp_repair(osv.osv):
    _name = 'mrp.repair'
    _inherit = 'mail.thread'
    _description = 'Repair Order'

    def _amount_untaxed(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates untaxed amount.
        @param self: The object pointer
        @param cr: The current row, from the database cursor,
        @param uid: The current user ID for security checks
        @param ids: List of selected IDs
        @param field_name: Name of field.
        @param arg: Argument
        @param context: A standard dictionary for contextual values
        @return: Dictionary of values.
        """
        res = {}
        cur_obj = self.pool.get('res.currency')

        for repair in self.browse(cr, uid, ids, context=context):
            res[repair.id] = 0.0
            for line in repair.operations:
                res[repair.id] += line.price_subtotal
            for line in repair.fees_lines:
                res[repair.id] += line.price_subtotal
            cur = repair.pricelist_id.currency_id
            res[repair.id] = cur_obj.round(cr, uid, cur, res[repair.id])
        return res

    def _amount_tax(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates taxed amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        #return {}.fromkeys(ids, 0)
        cur_obj = self.pool.get('res.currency')
        tax_obj = self.pool.get('account.tax')
        for repair in self.browse(cr, uid, ids, context=context):
            val = 0.0
            cur = repair.pricelist_id.currency_id
            for line in repair.operations:
                #manage prices with tax included use compute_all instead of compute
                if line.to_invoice and line.tax_id:
                    tax_calculate = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur, line.product_uom_qty, line.product_id.id, repair.partner_id.id)
                    for c in tax_calculate['taxes']:
                        val += c['amount']
            for line in repair.fees_lines:
                if line.to_invoice and line.tax_id:
                    tax_calculate = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur, line.product_uom_qty, line.product_id.id, repair.partner_id.id)
                    for c in tax_calculate['taxes']:
                        val += c['amount']
            res[repair.id] = cur_obj.round(cr, uid, cur, val)
        return res

    def _amount_total(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates total amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context=context)
        tax = self._amount_tax(cr, uid, ids, field_name, arg, context=context)
        cur_obj = self.pool.get('res.currency')
        for id in ids:
            repair = self.browse(cr, uid, id, context=context)
            cur = repair.pricelist_id.currency_id
            res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0))
        return res

    def _get_default_address(self, cr, uid, ids, field_name, arg, context=None):
        res = {}
        partner_obj = self.pool.get('res.partner')
        for data in self.browse(cr, uid, ids, context=context):
            adr_id = False
            if data.partner_id:
                adr_id = partner_obj.address_get(cr, uid, [data.partner_id.id], ['contact'])['contact']
            res[data.id] = adr_id
        return res

    def _get_lines(self, cr, uid, ids, context=None):
        return self.pool['mrp.repair'].search(cr, uid, [('operations', 'in', ids)], context=context)

    def _get_fee_lines(self, cr, uid, ids, context=None):
        return self.pool['mrp.repair'].search(cr, uid, [('fees_lines', 'in', ids)], context=context)

    _columns = {
        'name': fields.char('Repair Reference', required=True, states={'confirmed': [('readonly', True)]}, copy=False),
        'product_id': fields.many2one('product.product', string='Product to Repair', required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
                                    required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'partner_id': fields.many2one('res.partner', 'Partner', select=True, help='Choose partner for whom the order will be invoiced and delivered.', states={'confirmed': [('readonly', True)]}),
        'address_id': fields.many2one('res.partner', 'Delivery Address', domain="[('parent_id','=',partner_id)]", states={'confirmed': [('readonly', True)]}),
        'default_address_id': fields.function(_get_default_address, type="many2one", relation="res.partner"),
        'state': fields.selection([
            ('draft', 'Quotation'),
            ('cancel', 'Cancelled'),
            ('confirmed', 'Confirmed'),
            ('under_repair', 'Under Repair'),
            ('ready', 'Ready to Repair'),
            ('2binvoiced', 'To be Invoiced'),
            ('invoice_except', 'Invoice Exception'),
            ('done', 'Repaired')
            ], 'Status', readonly=True, track_visibility='onchange', copy=False,
            help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed repair order. \
            \n* The \'Confirmed\' status is used when a user confirms the repair order. \
            \n* The \'Ready to Repair\' status is used to start to repairing, user can start repairing only after repair order is confirmed. \
            \n* The \'To be Invoiced\' status is used to generate the invoice before or after repairing done. \
            \n* The \'Done\' status is set when repairing is completed.\
            \n* The \'Cancelled\' status is used when user cancel repair order.'),
        'location_id': fields.many2one('stock.location', 'Current Location', select=True, required=True, readonly=True, states={'draft': [('readonly', False)], 'confirmed': [('readonly', True)]}),
        'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, required=True, states={'draft': [('readonly', False)], 'confirmed': [('readonly', True)]}),
        'lot_id': fields.many2one('stock.production.lot', 'Repaired Lot', domain="[('product_id','=', product_id)]", help="Products repaired are all belonging to this lot", oldname="prodlot_id"),
        'guarantee_limit': fields.date('Warranty Expiration', states={'confirmed': [('readonly', True)]}),
        'operations': fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft': [('readonly', False)]}, copy=True),
        'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='Pricelist of the selected partner.'),
        'partner_invoice_id': fields.many2one('res.partner', 'Invoicing Address'),
        'invoice_method': fields.selection([
            ("none", "No Invoice"),
            ("b4repair", "Before Repair"),
            ("after_repair", "After Repair")
           ], "Invoice Method",
            select=True, required=True, states={'draft': [('readonly', False)]}, readonly=True, help='Selecting \'Before Repair\' or \'After Repair\' will allow you to generate invoice before or after the repair is done respectively. \'No invoice\' means you don\'t want to generate invoice for this repair order.'),
        'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True, track_visibility="onchange", copy=False),
        'move_id': fields.many2one('stock.move', 'Move', readonly=True, help="Move created by the repair order", track_visibility="onchange", copy=False),
        'fees_lines': fields.one2many('mrp.repair.fee', 'repair_id', 'Fees', readonly=True, states={'draft': [('readonly', False)]}, copy=True),
        'internal_notes': fields.text('Internal Notes'),
        'quotation_notes': fields.text('Quotation Notes'),
        'company_id': fields.many2one('res.company', 'Company'),
        'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
        'repaired': fields.boolean('Repaired', readonly=True, copy=False),
        'amount_untaxed': fields.function(_amount_untaxed, string='Untaxed Amount',
            store={
                'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
                'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
                'mrp.repair.fee': (_get_fee_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
            }),
        'amount_tax': fields.function(_amount_tax, string='Taxes',
            store={
                'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
                'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
                'mrp.repair.fee': (_get_fee_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
            }),
        'amount_total': fields.function(_amount_total, string='Total',
            store={
                'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
                'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
                'mrp.repair.fee': (_get_fee_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
            }),
    }

    def _default_stock_location(self, cr, uid, context=None):
        try:
            warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0')
            return warehouse.lot_stock_id.id
        except:
            return False

    _defaults = {
        'state': lambda *a: 'draft',
        'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').next_by_code(cr, uid, 'mrp.repair'),
        'invoice_method': lambda *a: 'none',
        'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.repair', context=context),
        'pricelist_id': lambda self, cr, uid, context: self.pool['product.pricelist'].search(cr, uid, [], limit=1)[0],
        'product_qty': 1.0,
        'location_id': _default_stock_location,
    }

    _sql_constraints = [
        ('name', 'unique (name)', 'The name of the Repair Order must be unique!'),
    ]

    def onchange_product_id(self, cr, uid, ids, product_id=None):
        """ On change of product sets some values.
        @param product_id: Changed product
        @return: Dictionary of values.
        """
        product = False
        if product_id:
            product = self.pool.get("product.product").browse(cr, uid, product_id)
        return {'value': {
                    'guarantee_limit': False,
                    'lot_id': False,
                    'product_uom': product and product.uom_id.id or False,
                }
        }

    def onchange_product_uom(self, cr, uid, ids, product_id, product_uom, context=None):
        res = {'value': {}}
        if not product_uom or not product_id:
            return res
        product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
        uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context)
        if uom.category_id.id != product.uom_id.category_id.id:
            res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')}
            res['value'].update({'product_uom': product.uom_id.id})
        return res

    def onchange_location_id(self, cr, uid, ids, location_id=None):
        """ On change of location
        """
        return {'value': {'location_dest_id': location_id}}

    def button_dummy(self, cr, uid, ids, context=None):
        return True

    def onchange_partner_id(self, cr, uid, ids, part, address_id):
        """ On change of partner sets the values of partner address,
        partner invoice address and pricelist.
        @param part: Changed id of partner.
        @param address_id: Address id from current record.
        @return: Dictionary of values.
        """
        part_obj = self.pool.get('res.partner')
        pricelist_obj = self.pool.get('product.pricelist')
        if not part:
            return {'value': {
                        'address_id': False,
                        'partner_invoice_id': False,
                        'pricelist_id': pricelist_obj.search(cr, uid, [], limit=1)[0]
                    }
            }
        addr = part_obj.address_get(cr, uid, [part], ['delivery', 'invoice', 'contact'])
        partner = part_obj.browse(cr, uid, part)
        pricelist = partner.property_product_pricelist and partner.property_product_pricelist.id or False
        return {'value': {
                    'address_id': addr['delivery'] or addr['contact'],
                    'partner_invoice_id': addr['invoice'],
                    'pricelist_id': pricelist
                }
        }

    def action_cancel_draft(self, cr, uid, ids, *args):
        """ Cancels repair order when it is in 'Draft' state.
        @param *arg: Arguments
        @return: True
        """
        if not len(ids):
            return False
        mrp_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids):
            mrp_line_obj.write(cr, uid, [l.id for l in repair.operations], {'state': 'draft'})
        self.write(cr, uid, ids, {'state': 'draft'})
        return self.create_workflow(cr, uid, ids)

    def action_confirm(self, cr, uid, ids, *args):
        """ Repair order state is set to 'To be invoiced' when invoice method
        is 'Before repair' else state becomes 'Confirmed'.
        @param *arg: Arguments
        @return: True
        """
        mrp_line_obj = self.pool.get('mrp.repair.line')
        for o in self.browse(cr, uid, ids):
            if (o.invoice_method == 'b4repair'):
                self.write(cr, uid, [o.id], {'state': '2binvoiced'})
            else:
                self.write(cr, uid, [o.id], {'state': 'confirmed'})
                for line in o.operations:
                    if line.product_id.tracking != 'none' and not line.lot_id:
                        raise UserError(_("Serial number is required for operation line with product '%s'") % (line.product_id.name))
                mrp_line_obj.write(cr, uid, [l.id for l in o.operations], {'state': 'confirmed'})
        return True

    def action_cancel(self, cr, uid, ids, context=None):
        """ Cancels repair order.
        @return: True
        """
        mrp_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            if not repair.invoiced:
                mrp_line_obj.write(cr, uid, [l.id for l in repair.operations], {'state': 'cancel'}, context=context)
            else:
                raise UserError(_('Repair order is already invoiced.'))
        return self.write(cr, uid, ids, {'state': 'cancel'})

    def wkf_invoice_create(self, cr, uid, ids, *args):
        self.action_invoice_create(cr, uid, ids)
        return True

    def action_invoice_create(self, cr, uid, ids, group=False, context=None):
        """ Creates invoice(s) for repair order.
        @param group: It is set to true when group invoice is to be generated.
        @return: Invoice Ids.
        """
        res = {}
        invoices_group = {}
        inv_line_obj = self.pool.get('account.invoice.line')
        inv_obj = self.pool.get('account.invoice')
        repair_line_obj = self.pool.get('mrp.repair.line')
        repair_fee_obj = self.pool.get('mrp.repair.fee')
        for repair in self.browse(cr, uid, ids, context=context):
            res[repair.id] = False
            if repair.state in ('draft', 'cancel') or repair.invoice_id:
                continue
            if not (repair.partner_id.id and repair.partner_invoice_id.id):
                raise UserError(_('You have to select a Partner Invoice Address in the repair form!'))
            comment = repair.quotation_notes
            if (repair.invoice_method != 'none'):
                if group and repair.partner_invoice_id.id in invoices_group:
                    inv_id = invoices_group[repair.partner_invoice_id.id]
                    invoice = inv_obj.browse(cr, uid, inv_id)
                    invoice_vals = {
                        'name': invoice.name + ', ' + repair.name,
                        'origin': invoice.origin + ', ' + repair.name,
                        'comment': (comment and (invoice.comment and invoice.comment + "\n" + comment or comment)) or (invoice.comment and invoice.comment or ''),
                    }
                    inv_obj.write(cr, uid, [inv_id], invoice_vals, context=context)
                else:
                    if not repair.partner_id.property_account_receivable_id:
                        raise UserError(_('No account defined for partner "%s".') % repair.partner_id.name)
                    account_id = repair.partner_id.property_account_receivable_id.id
                    inv = {
                        'name': repair.name,
                        'origin': repair.name,
                        'type': 'out_invoice',
                        'account_id': account_id,
                        'partner_id': repair.partner_invoice_id.id or repair.partner_id.id,
                        'currency_id': repair.pricelist_id.currency_id.id,
                        'comment': repair.quotation_notes,
                        'fiscal_position_id': repair.partner_id.property_account_position_id.id
                    }
                    inv_id = inv_obj.create(cr, uid, inv)
                    invoices_group[repair.partner_invoice_id.id] = inv_id
                self.write(cr, uid, repair.id, {'invoiced': True, 'invoice_id': inv_id})

                for operation in repair.operations:
                    if operation.to_invoice:
                        if group:
                            name = repair.name + '-' + operation.name
                        else:
                            name = operation.name

                        if operation.product_id.property_account_income_id:
                            account_id = operation.product_id.property_account_income_id.id
                        elif operation.product_id.categ_id.property_account_income_categ_id:
                            account_id = operation.product_id.categ_id.property_account_income_categ_id.id
                        else:
                            raise UserError(_('No account defined for product "%s".') % operation.product_id.name)

                        invoice_line_id = inv_line_obj.create(cr, uid, {
                            'invoice_id': inv_id,
                            'name': name,
                            'origin': repair.name,
                            'account_id': account_id,
                            'quantity': operation.product_uom_qty,
                            'invoice_line_tax_ids': [(6, 0, [x.id for x in operation.tax_id])],
                            'uom_id': operation.product_uom.id,
                            'price_unit': operation.price_unit,
                            'price_subtotal': operation.product_uom_qty * operation.price_unit,
                            'product_id': operation.product_id and operation.product_id.id or False
                        })
                        repair_line_obj.write(cr, uid, [operation.id], {'invoiced': True, 'invoice_line_id': invoice_line_id})
                for fee in repair.fees_lines:
                    if fee.to_invoice:
                        if group:
                            name = repair.name + '-' + fee.name
                        else:
                            name = fee.name
                        if not fee.product_id:
                            raise UserError(_('No product defined on Fees!'))

                        if fee.product_id.property_account_income_id:
                            account_id = fee.product_id.property_account_income_id.id
                        elif fee.product_id.categ_id.property_account_income_categ_id:
                            account_id = fee.product_id.categ_id.property_account_income_categ_id.id
                        else:
                            raise UserError(_('No account defined for product "%s".') % fee.product_id.name)

                        invoice_fee_id = inv_line_obj.create(cr, uid, {
                            'invoice_id': inv_id,
                            'name': name,
                            'origin': repair.name,
                            'account_id': account_id,
                            'quantity': fee.product_uom_qty,
                            'invoice_line_tax_ids': [(6, 0, [x.id for x in fee.tax_id])],
                            'uom_id': fee.product_uom.id,
                            'product_id': fee.product_id and fee.product_id.id or False,
                            'price_unit': fee.price_unit,
                            'price_subtotal': fee.product_uom_qty * fee.price_unit
                        })
                        repair_fee_obj.write(cr, uid, [fee.id], {'invoiced': True, 'invoice_line_id': invoice_fee_id})
                #inv_obj.button_reset_taxes(cr, uid, inv_id, context=context)
                res[repair.id] = inv_id
        return res

    def action_repair_ready(self, cr, uid, ids, context=None):
        """ Writes repair order state to 'Ready'
        @return: True
        """
        for repair in self.browse(cr, uid, ids, context=context):
            self.pool.get('mrp.repair.line').write(cr, uid, [l.id for
                    l in repair.operations], {'state': 'confirmed'}, context=context)
            self.write(cr, uid, [repair.id], {'state': 'ready'})
        return True

    def action_repair_start(self, cr, uid, ids, context=None):
        """ Writes repair order state to 'Under Repair'
        @return: True
        """
        repair_line = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            repair_line.write(cr, uid, [l.id for
                    l in repair.operations], {'state': 'confirmed'}, context=context)
            repair.write({'state': 'under_repair'})
        return True

    def action_repair_end(self, cr, uid, ids, context=None):
        """ Writes repair order state to 'To be invoiced' if invoice method is
        After repair else state is set to 'Ready'.
        @return: True
        """
        for order in self.browse(cr, uid, ids, context=context):
            val = {}
            val['repaired'] = True
            if (not order.invoiced and order.invoice_method == 'after_repair'):
                val['state'] = '2binvoiced'
            elif (not order.invoiced and order.invoice_method == 'b4repair'):
                val['state'] = 'ready'
            else:
                pass
            self.write(cr, uid, [order.id], val)
        return True

    def wkf_repair_done(self, cr, uid, ids, *args):
        self.action_repair_done(cr, uid, ids)
        return True

    def action_repair_done(self, cr, uid, ids, context=None):
        """ Creates stock move for operation and stock move for final product of repair order.
        @return: Move ids of final products
        """
        res = {}
        move_obj = self.pool.get('stock.move')
        repair_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            move_ids = []
            for move in repair.operations:
                move_id = move_obj.create(cr, uid, {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'restrict_lot_id': move.lot_id.id,
                    'product_uom_qty': move.product_uom_qty,
                    'product_uom': move.product_uom.id,
                    'partner_id': repair.address_id and repair.address_id.id or False,
                    'location_id': move.location_id.id,
                    'location_dest_id': move.location_dest_id.id,
                })
                move_ids.append(move_id)
                repair_line_obj.write(cr, uid, [move.id], {'move_id': move_id, 'state': 'done'}, context=context)
            move_id = move_obj.create(cr, uid, {
                'name': repair.name,
                'product_id': repair.product_id.id,
                'product_uom': repair.product_uom.id or repair.product_id.uom_id.id,
                'product_uom_qty': repair.product_qty,
                'partner_id': repair.address_id and repair.address_id.id or False,
                'location_id': repair.location_id.id,
                'location_dest_id': repair.location_dest_id.id,
                'restrict_lot_id': repair.lot_id.id,
            })
            move_ids.append(move_id)
            move_obj.action_done(cr, uid, move_ids, context=context)
            self.write(cr, uid, [repair.id], {'state': 'done', 'move_id': move_id}, context=context)
            res[repair.id] = move_id
        return res
Exemplo n.º 24
0
class mrp_repair_line(osv.osv, ProductChangeMixin):
    _name = 'mrp.repair.line'
    _description = 'Repair Line'

    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        tax_obj = self.pool.get('account.tax')
        # cur_obj = self.pool.get('res.currency')
        for line in self.browse(cr, uid, ids, context=context):
            if line.to_invoice:
                cur = line.repair_id.pricelist_id.currency_id
                taxes = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur.id, line.product_uom_qty, line.product_id.id, line.repair_id.partner_id.id)
                #res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
                res[line.id] = taxes['total_included']
            else:
                res[line.id] = 0
        return res

    _columns = {
        'name': fields.char('Description', required=True),
        'repair_id': fields.many2one('mrp.repair', 'Repair Order Reference', ondelete='cascade', select=True),
        'type': fields.selection([('add', 'Add'), ('remove', 'Remove')], 'Type', required=True),
        'to_invoice': fields.boolean('To Invoice'),
        'product_id': fields.many2one('product.product', 'Product', required=True),
        'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
        'price_unit': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price')),
        'price_subtotal': fields.function(_amount_line, string='Subtotal', digits=0),
        'tax_id': fields.many2many('account.tax', 'repair_operation_line_tax', 'repair_operation_line_id', 'tax_id', 'Taxes'),
        'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
        'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True, copy=False),
        'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True),
        'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True),
        'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True, copy=False),
        'lot_id': fields.many2one('stock.production.lot', 'Lot'),
        'state': fields.selection([
                    ('draft', 'Draft'),
                    ('confirmed', 'Confirmed'),
                    ('done', 'Done'),
                    ('cancel', 'Cancelled')], 'Status', required=True, readonly=True, copy=False,
                    help=' * The \'Draft\' status is set automatically as draft when repair order in draft status. \
                        \n* The \'Confirmed\' status is set automatically as confirm when repair order in confirm status. \
                        \n* The \'Done\' status is set automatically when repair order is completed.\
                        \n* The \'Cancelled\' status is set automatically when user cancel repair order.'),
    }
    _defaults = {
        'state': lambda *a: 'draft',
        'product_uom_qty': lambda *a: 1,
    }

    def onchange_operation_type(self, cr, uid, ids, type, guarantee_limit, company_id=False, context=None):
        """ On change of operation type it sets source location, destination location
        and to invoice field.
        @param product: Changed operation type.
        @param guarantee_limit: Guarantee limit of current record.
        @return: Dictionary of values.
        """
        if not type:
            return {'value': {
                'location_id': False,
                'location_dest_id': False
                }}
        location_obj = self.pool.get('stock.location')
        warehouse_obj = self.pool.get('stock.warehouse')
        location_id = location_obj.search(cr, uid, [('usage', '=', 'production')], context=context)
        location_id = location_id and location_id[0] or False

        if type == 'add':
            # TOCHECK: Find stock location for user's company warehouse or
            # repair order's company's warehouse (company_id field is added in fix of lp:831583)
            args = company_id and [('company_id', '=', company_id)] or []
            warehouse_ids = warehouse_obj.search(cr, uid, args, context=context)
            stock_id = False
            if warehouse_ids:
                stock_id = warehouse_obj.browse(cr, uid, warehouse_ids[0], context=context).lot_stock_id.id
            to_invoice = (guarantee_limit and datetime.strptime(guarantee_limit, '%Y-%m-%d') < datetime.now())

            return {'value': {
                'to_invoice': to_invoice,
                'location_id': stock_id,
                'location_dest_id': location_id
                }}
        scrap_location_ids = location_obj.search(cr, uid, [('scrap_location', '=', True)], context=context)

        return {'value': {
                'to_invoice': False,
                'location_id': location_id,
                'location_dest_id': scrap_location_ids and scrap_location_ids[0] or False,
                }}
Exemplo n.º 25
0
class hr_attendance(osv.osv):
    _inherit = "hr.attendance"

    def _get_default_date(self, cr, uid, context=None):
        if context is None:
            context = {}
        if 'name' in context:
            return context['name'] + time.strftime(' %H:%M:%S')
        return time.strftime('%Y-%m-%d %H:%M:%S')

    def _get_hr_timesheet_sheet(self, cr, uid, ids, context=None):
        attendance_ids = []
        for ts in self.browse(cr, uid, ids, context=context):
            cr.execute(
                """
                        SELECT a.id
                          FROM hr_attendance a
                         INNER JOIN hr_employee e
                               INNER JOIN resource_resource r
                                       ON (e.resource_id = r.id)
                            ON (a.employee_id = e.id)
                         LEFT JOIN res_users u
                         ON r.user_id = u.id
                         LEFT JOIN res_partner p
                         ON u.partner_id = p.id
                         WHERE %(date_to)s >= date_trunc('day', a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))
                              AND %(date_from)s <= date_trunc('day', a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))
                              AND %(user_id)s = r.user_id
                         GROUP BY a.id""", {
                    'date_from': ts.date_from,
                    'date_to': ts.date_to,
                    'user_id': ts.employee_id.user_id.id,
                })
            attendance_ids.extend([row[0] for row in cr.fetchall()])
        return attendance_ids

    def _get_attendance_employee_tz(self,
                                    cr,
                                    uid,
                                    employee_id,
                                    date,
                                    context=None):
        """ Simulate timesheet in employee timezone

        Return the attendance date in string format in the employee
        tz converted from utc timezone as we consider date of employee
        timesheet is in employee timezone
        """
        employee_obj = self.pool['hr.employee']

        tz = False
        if employee_id:
            employee = employee_obj.browse(cr,
                                           uid,
                                           employee_id,
                                           context=context)
            tz = employee.user_id.partner_id.tz

        if not date:
            date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)

        att_tz = timezone(tz or 'utc')

        attendance_dt = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
        att_tz_dt = pytz.utc.localize(attendance_dt)
        att_tz_dt = att_tz_dt.astimezone(att_tz)
        # We take only the date omiting the hours as we compare with timesheet
        # date_from which is a date format thus using hours would lead to
        # be out of scope of timesheet
        att_tz_date_str = datetime.strftime(att_tz_dt,
                                            DEFAULT_SERVER_DATE_FORMAT)
        return att_tz_date_str

    def _get_current_sheet(self,
                           cr,
                           uid,
                           employee_id,
                           date=False,
                           context=None):

        sheet_obj = self.pool['hr_timesheet_sheet.sheet']
        if not date:
            date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)

        att_tz_date_str = self._get_attendance_employee_tz(cr,
                                                           uid,
                                                           employee_id,
                                                           date=date,
                                                           context=context)
        sheet_ids = sheet_obj.search(cr,
                                     uid,
                                     [('date_from', '<=', att_tz_date_str),
                                      ('date_to', '>=', att_tz_date_str),
                                      ('employee_id', '=', employee_id)],
                                     limit=1,
                                     context=context)
        return sheet_ids and sheet_ids[0] or False

    def _sheet(self, cursor, user, ids, name, args, context=None):
        res = {}.fromkeys(ids, False)
        for attendance in self.browse(cursor, user, ids, context=context):
            res[attendance.id] = self._get_current_sheet(
                cursor,
                user,
                attendance.employee_id.id,
                attendance.name,
                context=context)
        return res

    _columns = {
        'sheet_id':
        fields.function(
            _sheet,
            string='Sheet',
            type='many2one',
            relation='hr_timesheet_sheet.sheet',
            store={
                'hr_timesheet_sheet.sheet':
                (_get_hr_timesheet_sheet,
                 ['employee_id', 'date_from', 'date_to'], 10),
                'hr.attendance': (lambda self, cr, uid, ids, context=None: ids,
                                  ['employee_id', 'name', 'day'], 10),
            },
        )
    }
    _defaults = {
        'name': _get_default_date,
    }

    def create(self, cr, uid, vals, context=None):
        if context is None:
            context = {}

        sheet_id = context.get('sheet_id') or self._get_current_sheet(
            cr,
            uid,
            vals.get('employee_id'),
            vals.get('name'),
            context=context)
        if sheet_id:
            att_tz_date_str = self._get_attendance_employee_tz(
                cr,
                uid,
                vals.get('employee_id'),
                date=vals.get('name'),
                context=context)
            ts = self.pool.get('hr_timesheet_sheet.sheet').browse(
                cr, uid, sheet_id, context=context)
            if ts.state not in ('draft', 'new'):
                raise UserError(
                    _('You can not enter an attendance in a submitted timesheet. Ask your manager to reset it before adding attendance.'
                      ))
            elif ts.date_from > att_tz_date_str or ts.date_to < att_tz_date_str:
                raise UserError(
                    _('You can not enter an attendance date outside the current timesheet dates.'
                      ))
        return super(hr_attendance, self).create(cr,
                                                 uid,
                                                 vals,
                                                 context=context)

    def unlink(self, cr, uid, ids, *args, **kwargs):
        if isinstance(ids, (int, long)):
            ids = [ids]
        self._check(cr, uid, ids)
        return super(hr_attendance, self).unlink(cr, uid, ids, *args, **kwargs)

    def write(self, cr, uid, ids, vals, context=None):
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        self._check(cr, uid, ids)
        res = super(hr_attendance, self).write(cr,
                                               uid,
                                               ids,
                                               vals,
                                               context=context)
        if 'sheet_id' in context:
            for attendance in self.browse(cr, uid, ids, context=context):
                if context['sheet_id'] != attendance.sheet_id.id:
                    raise UserError(_('You cannot enter an attendance ' \
                            'date outside the current timesheet dates.'))
        return res

    def _check(self, cr, uid, ids):
        for att in self.browse(cr, uid, ids):
            if att.sheet_id and att.sheet_id.state not in ('draft', 'new'):
                raise UserError(
                    _('You cannot modify an entry in a confirmed timesheet'))
        return True
Exemplo n.º 26
0
class task(osv.osv):
    _inherit = "project.task"

    # Compute: effective_hours, total_hours, progress
    def _hours_get(self, cr, uid, ids, field_names, args, context=None):
        res = {}
        tasks_data = self.pool['account.analytic.line'].read_group(
            cr,
            uid, [('task_id', 'in', ids)], ['task_id', 'unit_amount'],
            ['task_id'],
            context=context)
        for data in tasks_data:
            task = self.browse(cr, uid, data['task_id'][0], context=context)
            res[data['task_id'][0]] = {
                'effective_hours':
                data.get('unit_amount', 0.0),
                'remaining_hours':
                task.planned_hours - data.get('unit_amount', 0.0)
            }
            res[data['task_id'][0]]['total_hours'] = res[
                data['task_id'][0]]['remaining_hours'] + data.get(
                    'unit_amount', 0.0)
            res[data['task_id'][0]]['delay_hours'] = res[
                data['task_id'][0]]['total_hours'] - task.planned_hours
            res[data['task_id'][0]]['progress'] = 0.0
            if (task.planned_hours > 0.0 and data.get('unit_amount', 0.0)):
                res[data['task_id'][0]]['progress'] = round(
                    min(
                        100.0 * data.get('unit_amount', 0.0) /
                        task.planned_hours, 99.99), 2)
            # TDE CHECK: if task.state in ('done','cancelled'):
            if task.stage_id and task.stage_id.fold:
                res[data['task_id'][0]]['progress'] = 100.0
        return res

    def _get_task(self, cr, uid, id, context=None):
        res = []
        for line in self.pool.get('account.analytic.line').search_read(
                cr,
                uid, [('task_id', '!=', False), ('id', 'in', id)],
                context=context):
            res.append(line['task_id'][0])
        return res

    def _get_total_hours(self):
        return super(task, self)._get_total_hours() + self.effective_hours

    _columns = {
        'remaining_hours':
        fields.function(
            _hours_get,
            string='Remaining Hours',
            multi='line_id',
            help=
            "Total remaining time, can be re-estimated periodically by the assignee of the task.",
            store={
                'project.task':
                (lambda self, cr, uid, ids, c={}: ids,
                 ['timesheet_ids', 'remaining_hours', 'planned_hours'], 10),
                'account.analytic.line':
                (_get_task, ['task_id', 'unit_amount'], 10),
            }),
        'effective_hours':
        fields.function(_hours_get,
                        string='Hours Spent',
                        multi='line_id',
                        help="Computed using the sum of the task work done.",
                        store={
                            'project.task':
                            (lambda self, cr, uid, ids, c={}: ids, [
                                'timesheet_ids', 'remaining_hours',
                                'planned_hours'
                            ], 10),
                            'account.analytic.line':
                            (_get_task, ['task_id', 'unit_amount'], 10),
                        }),
        'total_hours':
        fields.function(_hours_get,
                        string='Total',
                        multi='line_id',
                        help="Computed as: Time Spent + Remaining Time.",
                        store={
                            'project.task':
                            (lambda self, cr, uid, ids, c={}: ids, [
                                'timesheet_ids', 'remaining_hours',
                                'planned_hours'
                            ], 10),
                            'account.analytic.line':
                            (_get_task, ['task_id', 'unit_amount'], 10),
                        }),
        'progress':
        fields.function(
            _hours_get,
            string='Working Time Progress (%)',
            multi='line_id',
            group_operator="avg",
            help=
            "If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time",
            store={
                'project.task': (lambda self, cr, uid, ids, c={}: ids, [
                    'timesheet_ids', 'remaining_hours', 'planned_hours',
                    'state', 'stage_id'
                ], 10),
                'account.analytic.line':
                (_get_task, ['task_id', 'unit_amount'], 10),
            }),
        'delay_hours':
        fields.function(
            _hours_get,
            string='Delay Hours',
            multi='line_id',
            help=
            "Computed as difference between planned hours by the project manager and the total hours of the task.",
            store={
                'project.task':
                (lambda self, cr, uid, ids, c={}: ids,
                 ['timesheet_ids', 'remaining_hours', 'planned_hours'], 10),
                'account.analytic.line':
                (_get_task, ['task_id', 'unit_amount'], 10),
            }),
        'timesheet_ids':
        fields.one2many('account.analytic.line', 'task_id', 'Timesheets'),
        'analytic_account_id':
        fields.related('project_id',
                       'analytic_account_id',
                       type='many2one',
                       relation='account.analytic.account',
                       string='Analytic Account',
                       store=True),
    }

    _defaults = {
        'progress': 0,
    }

    def _prepare_delegate_values(self,
                                 cr,
                                 uid,
                                 ids,
                                 delegate_data,
                                 context=None):
        vals = super(task,
                     self)._prepare_delegate_values(cr, uid, ids,
                                                    delegate_data, context)
        for task in self.browse(cr, uid, ids, context=context):
            vals[task.id]['planned_hours'] += task.effective_hours
        return vals

    def onchange_project(self, cr, uid, ids, project_id, context=None):
        result = super(task, self).onchange_project(cr,
                                                    uid,
                                                    ids,
                                                    project_id,
                                                    context=context)
        if not project_id:
            return result
        if 'value' not in result:
            result['value'] = {}
        project = self.pool['project.project'].browse(cr,
                                                      uid,
                                                      project_id,
                                                      context=context)
        return result
Exemplo n.º 27
0
class mrp_operations_operation(osv.osv):
    _name = "mrp_operations.operation"

    def _order_date_search_production(self, cr, uid, ids, context=None):
        """ Finds operations for a production order.
        @return: List of ids
        """
        operation_ids = self.pool.get('mrp_operations.operation').search(
            cr, uid, [('production_id', '=', ids[0])], context=context)
        return operation_ids

    def _get_order_date(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates planned date for an operation.
        @return: Dictionary of values
        """
        res = {}
        operation_obj = self.browse(cr, uid, ids, context=context)
        for operation in operation_obj:
            res[operation.id] = operation.production_id.date_planned
        return res

    def calc_delay(self, cr, uid, vals):
        """ Calculates delay of work order.
        @return: Delay
        """
        code_lst = []
        time_lst = []

        code_ids = self.pool.get('mrp_operations.operation.code').search(
            cr, uid, [('id', '=', vals['code_id'])])
        code = self.pool.get('mrp_operations.operation.code').browse(
            cr, uid, code_ids)[0]

        oper_ids = self.search(cr, uid,
                               [('production_id', '=', vals['production_id']),
                                ('workcenter_id', '=', vals['workcenter_id'])])
        oper_objs = self.browse(cr, uid, oper_ids)

        for oper in oper_objs:
            code_lst.append(oper.code_id.start_stop)
            time_lst.append(oper.date_start)

        code_lst.append(code.start_stop)
        time_lst.append(vals['date_start'])
        diff = 0
        for i in range(0, len(code_lst)):
            if code_lst[i] == 'pause' or code_lst[i] == 'done' or code_lst[
                    i] == 'cancel':
                if not i: continue
                if code_lst[i - 1] not in ('resume', 'start'):
                    continue
                a = datetime.strptime(time_lst[i - 1], '%Y-%m-%d %H:%M:%S')
                b = datetime.strptime(time_lst[i], '%Y-%m-%d %H:%M:%S')
                diff += (b - a).days * 24
                diff += (b - a).seconds / float(60 * 60)
        return diff

    def check_operation(self, cr, uid, vals):
        """ Finds which operation is called ie. start, pause, done, cancel.
        @param vals: Dictionary of values.
        @return: True or False
        """
        code_ids = self.pool.get('mrp_operations.operation.code').search(
            cr, uid, [('id', '=', vals['code_id'])])
        code = self.pool.get('mrp_operations.operation.code').browse(
            cr, uid, code_ids)[0]
        code_lst = []
        oper_ids = self.search(cr, uid,
                               [('production_id', '=', vals['production_id']),
                                ('workcenter_id', '=', vals['workcenter_id'])])
        oper_objs = self.browse(cr, uid, oper_ids)

        if not oper_objs:
            if code.start_stop != 'start':
                raise UserError(_('Operation is not started yet!'))
                return False
        else:
            for oper in oper_objs:
                code_lst.append(oper.code_id.start_stop)
            if code.start_stop == 'start':
                if 'start' in code_lst:
                    raise UserError(
                        _('Operation has already started! You can either Pause/Finish/Cancel the operation.'
                          ))
                    return False
            if code.start_stop == 'pause':
                if code_lst[len(code_lst) - 1] != 'resume' and code_lst[
                        len(code_lst) - 1] != 'start':
                    raise UserError(
                        _('In order to Pause the operation, it must be in the Start or Resume state!'
                          ))
                    return False
            if code.start_stop == 'resume':
                if code_lst[len(code_lst) - 1] != 'pause':
                    raise UserError(
                        _('In order to Resume the operation, it must be in the Pause state!'
                          ))
                    return False

            if code.start_stop == 'done':
                if code_lst[len(code_lst) - 1] != 'start' and code_lst[
                        len(code_lst) - 1] != 'resume':
                    raise UserError(
                        _('In order to Finish the operation, it must be in the Start or Resume state!'
                          ))
                    return False
                if 'cancel' in code_lst:
                    raise UserError(_('Operation is Already Cancelled!'))
                    return False
            if code.start_stop == 'cancel':
                if not 'start' in code_lst:
                    raise UserError(_('No operation to cancel.'))
                    return False
                if 'done' in code_lst:
                    raise UserError(_('Operation is already finished!'))
                    return False
        return True

    def write(self, cr, uid, ids, vals, context=None):
        oper_objs = self.browse(cr, uid, ids, context=context)[0]
        vals['production_id'] = oper_objs.production_id.id
        vals['workcenter_id'] = oper_objs.workcenter_id.id

        if 'code_id' in vals:
            self.check_operation(cr, uid, vals)

        if 'date_start' in vals:
            vals['date_start'] = vals['date_start']
            vals['code_id'] = oper_objs.code_id.id
            delay = self.calc_delay(cr, uid, vals)
            wc_op_id = self.pool.get('mrp.production.workcenter.line').search(
                cr, uid, [('workcenter_id', '=', vals['workcenter_id']),
                          ('production_id', '=', vals['production_id'])])
            self.pool.get('mrp.production.workcenter.line').write(
                cr, uid, wc_op_id, {'delay': delay})

        return super(mrp_operations_operation, self).write(cr,
                                                           uid,
                                                           ids,
                                                           vals,
                                                           context=context)

    def create(self, cr, uid, vals, context=None):
        workcenter_pool = self.pool.get('mrp.production.workcenter.line')
        code_ids = self.pool.get('mrp_operations.operation.code').search(
            cr, uid, [('id', '=', vals['code_id'])])
        code = self.pool.get('mrp_operations.operation.code').browse(
            cr, uid, code_ids, context=context)[0]
        wc_op_id = workcenter_pool.search(
            cr, uid, [('workcenter_id', '=', vals['workcenter_id']),
                      ('production_id', '=', vals['production_id'])])
        if code.start_stop in ('start', 'done', 'pause', 'cancel', 'resume'):
            if not wc_op_id:
                production_obj = self.pool.get('mrp.production').browse(
                    cr, uid, vals['production_id'], context=context)
                wc_op_id.append(
                    workcenter_pool.create(
                        cr, uid, {
                            'production_id': vals['production_id'],
                            'name': production_obj.product_id.name,
                            'workcenter_id': vals['workcenter_id']
                        }))
            if code.start_stop == 'start':
                workcenter_pool.action_start_working(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_start_working')

            if code.start_stop == 'done':
                workcenter_pool.action_done(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_done')
                self.pool.get('mrp.production').write(
                    cr, uid, vals['production_id'], {
                        'date_finished':
                        datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                    })

            if code.start_stop == 'pause':
                workcenter_pool.action_pause(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_pause')

            if code.start_stop == 'resume':
                workcenter_pool.action_resume(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_resume')

            if code.start_stop == 'cancel':
                workcenter_pool.action_cancel(cr, uid, wc_op_id)
                workcenter_pool.signal_workflow(cr, uid, [wc_op_id[0]],
                                                'button_cancel')

        if not self.check_operation(cr, uid, vals):
            return
        delay = self.calc_delay(cr, uid, vals)
        line_vals = {}
        line_vals['delay'] = delay
        if vals.get('date_start', False):
            if code.start_stop == 'done':
                line_vals['date_finished'] = vals['date_start']
            elif code.start_stop == 'start':
                line_vals['date_start'] = vals['date_start']

        self.pool.get('mrp.production.workcenter.line').write(cr,
                                                              uid,
                                                              wc_op_id,
                                                              line_vals,
                                                              context=context)

        return super(mrp_operations_operation, self).create(cr,
                                                            uid,
                                                            vals,
                                                            context=context)

    def initialize_workflow_instance(self, cr, uid, context=None):
        mrp_production_workcenter_line = self.pool.get(
            'mrp.production.workcenter.line')
        line_ids = mrp_production_workcenter_line.search(cr,
                                                         uid, [],
                                                         context=context)
        mrp_production_workcenter_line.create_workflow(cr, uid, line_ids)
        return True

    _columns = {
        'production_id':
        fields.many2one('mrp.production', 'Production', required=True),
        'workcenter_id':
        fields.many2one('mrp.workcenter', 'Work Center', required=True),
        'code_id':
        fields.many2one('mrp_operations.operation.code', 'Code',
                        required=True),
        'date_start':
        fields.datetime('Start Date'),
        'date_finished':
        fields.datetime('End Date'),
        'order_date':
        fields.function(_get_order_date,
                        string='Order Date',
                        type='date',
                        store={
                            'mrp.production': (_order_date_search_production,
                                               ['date_planned'], 10)
                        }),
    }
    _defaults = {
        'date_start': lambda *a: datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    }
Exemplo n.º 28
0
class mrp_production_workcenter_line(osv.osv):
    def _get_date_end(self, cr, uid, ids, field_name, arg, context=None):
        """ Finds ending date.
        @return: Dictionary of values.
        """
        ops = self.browse(cr, uid, ids, context=context)
        date_and_hours_by_cal = [(op.date_planned, op.hour,
                                  op.workcenter_id.calendar_id.id)
                                 for op in ops if op.date_planned]

        intervals = self.pool.get('resource.calendar').interval_get_multi(
            cr, uid, date_and_hours_by_cal)

        res = {}
        for op in ops:
            res[op.id] = False
            if op.date_planned:
                i = intervals.get((op.date_planned, op.hour,
                                   op.workcenter_id.calendar_id.id))
                if i:
                    res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S')
                else:
                    res[op.id] = op.date_planned
        return res

    def onchange_production_id(self,
                               cr,
                               uid,
                               ids,
                               production_id,
                               context=None):
        if not production_id:
            return {}
        production = self.pool.get('mrp.production').browse(cr,
                                                            uid,
                                                            production_id,
                                                            context=None)
        result = {
            'product': production.product_id.id,
            'qty': production.product_qty,
            'uom': production.product_uom.id,
        }
        return {'value': result}

    _inherit = 'mrp.production.workcenter.line'
    _order = "sequence, date_planned"

    _columns = {
       'state': fields.selection([('draft','Draft'),('cancel','Cancelled'),('pause','Pending'),('startworking', 'In Progress'),('done','Finished')],'Status', readonly=True, copy=False,
                                 help="* When a work order is created it is set in 'Draft' status.\n" \
                                       "* When user sets work order in start mode that time it will be set in 'In Progress' status.\n" \
                                       "* When work order is in running mode, during that time if user wants to stop or to make changes in order then can set in 'Pending' status.\n" \
                                       "* When the user cancels the work order it will be set in 'Canceled' status.\n" \
                                       "* When order is completely processed that time it is set in 'Finished' status."),
       'date_planned': fields.datetime('Scheduled Date', select=True),
       'date_planned_end': fields.function(_get_date_end, string='End Date', type='datetime'),
       'date_start': fields.datetime('Start Date'),
       'date_finished': fields.datetime('End Date'),
       'delay': fields.float('Working Hours',help="The elapsed time between operation start and stop in this Work Center",readonly=True),
       'production_state':fields.related('production_id','state',
            type='selection',
            selection=[('draft','Draft'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],
            string='Production Status', readonly=True),
       'product':fields.related('production_id','product_id',type='many2one',relation='product.product',string='Product',
            readonly=True),
       'qty':fields.related('production_id','product_qty',type='float',string='Qty',readonly=True, store=True),
       'uom':fields.related('production_id','product_uom',type='many2one',relation='product.uom',string='Unit of Measure',readonly=True),
    }

    _defaults = {'state': 'draft', 'delay': 0.0, 'production_state': 'draft'}

    def modify_production_order_state(self, cr, uid, ids, action):
        """ Modifies production order state if work order state is changed.
        @param action: Action to perform.
        @return: Nothing
        """
        prod_obj_pool = self.pool.get('mrp.production')
        oper_obj = self.browse(cr, uid, ids)[0]
        prod_obj = oper_obj.production_id
        if action == 'start':
            if prod_obj.state == 'confirmed':
                prod_obj_pool.force_production(cr, uid, [prod_obj.id])
                prod_obj_pool.signal_workflow(cr, uid, [prod_obj.id],
                                              'button_produce')
            elif prod_obj.state == 'ready':
                prod_obj_pool.signal_workflow(cr, uid, [prod_obj.id],
                                              'button_produce')
            elif prod_obj.state == 'in_production':
                return
            else:
                raise UserError(
                    _('Manufacturing order cannot be started in state "%s"!') %
                    (prod_obj.state, ))
        else:
            open_count = self.search_count(
                cr, uid, [('production_id', '=', prod_obj.id),
                          ('state', '!=', 'done')])
            flag = not bool(open_count)
            if flag:
                button_produce_done = True
                for production in prod_obj_pool.browse(cr,
                                                       uid, [prod_obj.id],
                                                       context=None):
                    if production.move_lines or production.move_created_ids:
                        moves = production.move_lines + production.move_created_ids
                        # If tracking is activated, we want to make sure the user will enter the
                        # serial numbers.
                        if moves.filtered(
                                lambda r: r.product_id.tracking != 'none'):
                            button_produce_done = False
                        else:
                            prod_obj_pool.action_produce(
                                cr,
                                uid,
                                production.id,
                                production.product_qty,
                                'consume_produce',
                                context=None)
                if button_produce_done:
                    prod_obj_pool.signal_workflow(cr, uid,
                                                  [oper_obj.production_id.id],
                                                  'button_produce_done')
        return

    def write(self, cr, uid, ids, vals, context=None, update=True):
        result = super(mrp_production_workcenter_line,
                       self).write(cr, uid, ids, vals, context=context)
        prod_obj = self.pool.get('mrp.production')
        if vals.get('date_planned', False) and update:
            for prod in self.browse(cr, uid, ids, context=context):
                if prod.production_id.workcenter_lines:
                    dstart = min(
                        vals['date_planned'],
                        prod.production_id.workcenter_lines[0]['date_planned'])
                    prod_obj.write(cr,
                                   uid, [prod.production_id.id],
                                   {'date_start': dstart},
                                   context=context,
                                   mini=False)
        return result

    def action_draft(self, cr, uid, ids, context=None):
        """ Sets state to draft.
        @return: True
        """
        return self.write(cr, uid, ids, {'state': 'draft'}, context=context)

    def action_start_working(self, cr, uid, ids, context=None):
        """ Sets state to start working and writes starting date.
        @return: True
        """
        self.modify_production_order_state(cr, uid, ids, 'start')
        self.write(cr,
                   uid,
                   ids, {
                       'state': 'startworking',
                       'date_start': time.strftime('%Y-%m-%d %H:%M:%S')
                   },
                   context=context)
        return True

    def action_done(self, cr, uid, ids, context=None):
        """ Sets state to done, writes finish date and calculates delay.
        @return: True
        """
        delay = 0.0
        date_now = time.strftime('%Y-%m-%d %H:%M:%S')
        obj_line = self.browse(cr, uid, ids[0])

        date_start = datetime.strptime(obj_line.date_start,
                                       '%Y-%m-%d %H:%M:%S')
        date_finished = datetime.strptime(date_now, '%Y-%m-%d %H:%M:%S')
        delay += (date_finished - date_start).days * 24
        delay += (date_finished - date_start).seconds / float(60 * 60)

        self.write(cr,
                   uid,
                   ids, {
                       'state': 'done',
                       'date_finished': date_now,
                       'delay': delay
                   },
                   context=context)
        self.modify_production_order_state(cr, uid, ids, 'done')
        return True

    def action_cancel(self, cr, uid, ids, context=None):
        """ Sets state to cancel.
        @return: True
        """
        return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)

    def action_pause(self, cr, uid, ids, context=None):
        """ Sets state to pause.
        @return: True
        """
        return self.write(cr, uid, ids, {'state': 'pause'}, context=context)

    def action_resume(self, cr, uid, ids, context=None):
        """ Sets state to startworking.
        @return: True
        """
        return self.write(cr,
                          uid,
                          ids, {'state': 'startworking'},
                          context=context)
Exemplo n.º 29
0
class gamification_goal_definition(osv.Model):
    """Goal definition

    A goal definition contains the way to evaluate an objective
    Each module wanting to be able to set goals to the users needs to create
    a new gamification_goal_definition
    """
    _name = 'gamification.goal.definition'
    _description = 'Gamification goal definition'

    def _get_suffix(self, cr, uid, ids, field_name, arg, context=None):
        res = dict.fromkeys(ids, '')
        for goal in self.browse(cr, uid, ids, context=context):
            if goal.suffix and not goal.monetary:
                res[goal.id] = goal.suffix
            elif goal.monetary:
                # use the current user's company currency
                user = self.pool.get('res.users').browse(cr, uid, uid, context)
                if goal.suffix:
                    res[goal.id] = "%s %s" % (
                        user.company_id.currency_id.symbol, goal.suffix)
                else:
                    res[goal.id] = user.company_id.currency_id.symbol
            else:
                res[goal.id] = ""
        return res

    _columns = {
        'name':
        fields.char('Goal Definition', required=True, translate=True),
        'description':
        fields.text('Goal Description'),
        'monetary':
        fields.boolean(
            'Monetary Value',
            help=
            "The target and current value are defined in the company currency."
        ),
        'suffix':
        fields.char('Suffix',
                    help="The unit of the target and current values",
                    translate=True),
        'full_suffix':
        fields.function(_get_suffix,
                        type="char",
                        string="Full Suffix",
                        help="The currency and suffix field"),
        'computation_mode':
        fields.selection(
            [
                ('manually', 'Recorded manually'),
                ('count', 'Automatic: number of records'),
                ('sum', 'Automatic: sum on a field'),
                ('python', 'Automatic: execute a specific Python code'),
            ],
            string="Computation Mode",
            help=
            "Defined how will be computed the goals. The result of the operation will be stored in the field 'Current'.",
            required=True),
        'display_mode':
        fields.selection([
            ('progress', 'Progressive (using numerical values)'),
            ('boolean', 'Exclusive (done or not-done)'),
        ],
                         string="Displayed as",
                         required=True),
        'model_id':
        fields.many2one('ir.model',
                        string='Model',
                        help='The model object for the field to evaluate'),
        'model_inherited_model_ids':
        fields.related('model_id',
                       'inherited_model_ids',
                       type="many2many",
                       obj="ir.model",
                       string="Inherited models",
                       readonly="True"),
        'field_id':
        fields.many2one('ir.model.fields',
                        string='Field to Sum',
                        help='The field containing the value to evaluate'),
        'field_date_id':
        fields.many2one('ir.model.fields',
                        string='Date Field',
                        help='The date to use for the time period evaluated'),
        'domain':
        fields.char(
            "Filter Domain",
            help=
            "Domain for filtering records. General rule, not user depending, e.g. [('state', '=', 'done')]. The expression can contain reference to 'user' which is a browse record of the current user if not in batch mode.",
            required=True),
        'batch_mode':
        fields.boolean(
            'Batch Mode',
            help=
            "Evaluate the expression in batch instead of once for each user"),
        'batch_distinctive_field':
        fields.many2one(
            'ir.model.fields',
            string="Distinctive field for batch user",
            help=
            "In batch mode, this indicates which field distinct one user form the other, e.g. user_id, partner_id..."
        ),
        'batch_user_expression':
        fields.char(
            "Evaluted expression for batch mode",
            help=
            "The value to compare with the distinctive field. The expression can contain reference to 'user' which is a browse record of the current user, e.g. user.id, user.partner_id.id..."
        ),
        'compute_code':
        fields.text(
            'Python Code',
            help=
            "Python code to be executed for each user. 'result' should contains the new current value. Evaluated user can be access through object.user_id."
        ),
        'condition':
        fields.selection(
            [('higher', 'The higher the better'),
             ('lower', 'The lower the better')],
            string='Goal Performance',
            help=
            'A goal is considered as completed when the current value is compared to the value to reach',
            required=True),
        'action_id':
        fields.many2one(
            'ir.actions.act_window',
            string="Action",
            help="The action that will be called to update the goal value."),
        'res_id_field':
        fields.char(
            "ID Field of user",
            help=
            "The field name on the user profile (res.users) containing the value for res_id for action."
        ),
    }

    _defaults = {
        'condition': 'higher',
        'computation_mode': 'manually',
        'domain': "[]",
        'monetary': False,
        'display_mode': 'progress',
    }

    def number_following(self,
                         cr,
                         uid,
                         model_name="mail.thread",
                         context=None):
        """Return the number of 'model_name' objects the user is following

        The model specified in 'model_name' must inherit from mail.thread
        """
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        return self.pool.get('mail.followers').search(
            cr,
            uid, [('res_model', '=', model_name),
                  ('partner_id', '=', user.partner_id.id)],
            count=True,
            context=context)

    def _check_domain_validity(self, cr, uid, ids, context=None):
        # take admin as should always be present
        superuser = self.pool['res.users'].browse(cr,
                                                  uid,
                                                  SUPERUSER_ID,
                                                  context=context)
        for definition in self.browse(cr, uid, ids, context=context):
            if definition.computation_mode not in ('count', 'sum'):
                continue

            obj = self.pool[definition.model_id.model]
            try:
                domain = safe_eval(definition.domain, {'user': superuser})
                # demmy search to make sure the domain is valid
                obj.search(cr, uid, domain, context=context, count=True)
            except (ValueError, SyntaxError), e:
                msg = e.message or (e.msg + '\n' + e.text)
                raise UserError(
                    _("The domain for the definition %s seems incorrect, please check it.\n\n%s"
                      % (definition.name, msg)))
        return True
Exemplo n.º 30
0
class gamification_goal(osv.Model):
    """Goal instance for a user

    An individual goal for a user on a specified time period"""

    _name = 'gamification.goal'
    _description = 'Gamification goal instance'

    def _get_completion(self, cr, uid, ids, field_name, arg, context=None):
        """Return the percentage of completeness of the goal, between 0 and 100"""
        res = dict.fromkeys(ids, 0.0)
        for goal in self.browse(cr, uid, ids, context=context):
            if goal.definition_condition == 'higher':
                if goal.current >= goal.target_goal:
                    res[goal.id] = 100.0
                else:
                    res[goal.id] = round(
                        100.0 * goal.current / goal.target_goal, 2)
            elif goal.current < goal.target_goal:
                # a goal 'lower than' has only two values possible: 0 or 100%
                res[goal.id] = 100.0
            else:
                res[goal.id] = 0.0
        return res

    def on_change_definition_id(self,
                                cr,
                                uid,
                                ids,
                                definition_id=False,
                                context=None):
        goal_definition = self.pool.get('gamification.goal.definition')
        if not definition_id:
            return {'value': {'definition_id': False}}
        goal_definition = goal_definition.browse(cr,
                                                 uid,
                                                 definition_id,
                                                 context=context)
        return {
            'value': {
                'computation_mode': goal_definition.computation_mode,
                'definition_condition': goal_definition.condition
            }
        }

    _columns = {
        'definition_id':
        fields.many2one('gamification.goal.definition',
                        string='Goal Definition',
                        required=True,
                        ondelete="cascade"),
        'user_id':
        fields.many2one('res.users',
                        string='User',
                        required=True,
                        auto_join=True,
                        ondelete="cascade"),
        'line_id':
        fields.many2one('gamification.challenge.line',
                        string='Challenge Line',
                        ondelete="cascade"),
        'challenge_id':
        fields.related(
            'line_id',
            'challenge_id',
            string="Challenge",
            type='many2one',
            relation='gamification.challenge',
            store=True,
            readonly=True,
            help=
            "Challenge that generated the goal, assign challenge to users to generate goals with a value in this field."
        ),
        'start_date':
        fields.date('Start Date'),
        'end_date':
        fields.date('End Date'),  # no start and end = always active
        'target_goal':
        fields.float('To Reach', required=True,
                     track_visibility='always'),  # no goal = global index
        'current':
        fields.float('Current Value', required=True,
                     track_visibility='always'),
        'completeness':
        fields.function(_get_completion, type='float', string='Completeness'),
        'state':
        fields.selection([
            ('draft', 'Draft'),
            ('inprogress', 'In progress'),
            ('reached', 'Reached'),
            ('failed', 'Failed'),
            ('canceled', 'Canceled'),
        ],
                         string='State',
                         required=True,
                         track_visibility='always'),
        'to_update':
        fields.boolean('To update'),
        'closed':
        fields.boolean('Closed goal',
                       help="These goals will not be recomputed."),
        'computation_mode':
        fields.related('definition_id',
                       'computation_mode',
                       type='char',
                       string="Computation mode"),
        'remind_update_delay':
        fields.integer(
            'Remind delay',
            help=
            "The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value is specified."
        ),
        'last_update':
        fields.date(
            'Last Update',
            help=
            "In case of manual goal, reminders are sent if the goal as not been updated for a while (defined in challenge). Ignored in case of non-manual goal or goal not linked to a challenge."
        ),
        'definition_description':
        fields.related('definition_id',
                       'description',
                       type='char',
                       string='Definition Description',
                       readonly=True),
        'definition_condition':
        fields.related('definition_id',
                       'condition',
                       type='char',
                       string='Definition Condition',
                       readonly=True),
        'definition_suffix':
        fields.related('definition_id',
                       'full_suffix',
                       type="char",
                       string="Suffix",
                       readonly=True),
        'definition_display':
        fields.related('definition_id',
                       'display_mode',
                       type="char",
                       string="Display Mode",
                       readonly=True),
    }

    _defaults = {
        'current': 0,
        'state': 'draft',
        'start_date': fields.date.today,
    }
    _order = 'start_date desc, end_date desc, definition_id, id'

    def _check_remind_delay(self, cr, uid, goal, context=None):
        """Verify if a goal has not been updated for some time and send a
        reminder message of needed.

        :return: data to write on the goal object
        """
        temp_obj = self.pool['mail.template']
        if goal.remind_update_delay and goal.last_update:
            delta_max = timedelta(days=goal.remind_update_delay)
            last_update = datetime.strptime(goal.last_update, DF).date()
            if date.today() - last_update > delta_max:
                # generate a remind report
                temp_obj = self.pool.get('mail.template')
                template_id = self.pool['ir.model.data'].get_object_reference(
                    cr, uid, 'gamification', 'email_template_goal_reminder')[0]
                template = temp_obj.get_email_template(cr,
                                                       uid,
                                                       template_id,
                                                       goal.id,
                                                       context=context)
                body_html = temp_obj.render_template(cr,
                                                     uid,
                                                     template.body_html,
                                                     'gamification.goal',
                                                     goal.id,
                                                     context=template._context)
                self.pool['mail.thread'].message_post(
                    cr,
                    uid,
                    0,
                    body=body_html,
                    partner_ids=[goal.user_id.partner_id.id],
                    context=context,
                    subtype='mail.mt_comment')
                return {'to_update': True}
        return {}

    def _get_write_values(self, cr, uid, goal, new_value, context=None):
        """Generate values to write after recomputation of a goal score"""
        if new_value == goal.current:
            # avoid useless write if the new value is the same as the old one
            return {}

        result = {goal.id: {'current': new_value}}
        if (goal.definition_id.condition == 'higher' and new_value >= goal.target_goal) \
          or (goal.definition_id.condition == 'lower' and new_value <= goal.target_goal):
            # success, do no set closed as can still change
            result[goal.id]['state'] = 'reached'

        elif goal.end_date and fields.date.today() > goal.end_date:
            # check goal failure
            result[goal.id]['state'] = 'failed'
            result[goal.id]['closed'] = True

        return result

    def update(self, cr, uid, ids, context=None):
        """Update the goals to recomputes values and change of states

        If a manual goal is not updated for enough time, the user will be
        reminded to do so (done only once, in 'inprogress' state).
        If a goal reaches the target value, the status is set to reached
        If the end date is passed (at least +1 day, time not considered) without
        the target value being reached, the goal is set as failed."""
        if context is None:
            context = {}
        commit = context.get('commit_gamification', False)

        goals_by_definition = {}
        for goal in self.browse(cr, uid, ids, context=context):
            goals_by_definition.setdefault(goal.definition_id, []).append(goal)

        for definition, goals in goals_by_definition.items():
            goals_to_write = dict((goal.id, {}) for goal in goals)
            if definition.computation_mode == 'manually':
                for goal in goals:
                    goals_to_write[goal.id].update(
                        self._check_remind_delay(cr, uid, goal, context))
            elif definition.computation_mode == 'python':
                # TODO batch execution
                for goal in goals:
                    # execute the chosen method
                    cxt = {
                        'self': self.pool.get('gamification.goal'),
                        'object': goal,
                        'pool': self.pool,
                        'cr': cr,
                        'context':
                        dict(context
                             ),  # copy context to prevent side-effects of eval
                        'uid': uid,
                        'date': date,
                        'datetime': datetime,
                        'timedelta': timedelta,
                        'time': time
                    }
                    code = definition.compute_code.strip()
                    safe_eval(code, cxt, mode="exec", nocopy=True)
                    # the result of the evaluated codeis put in the 'result' local variable, propagated to the context
                    result = cxt.get('result')
                    if result is not None and type(result) in (float, int,
                                                               long):
                        goals_to_write.update(
                            self._get_write_values(cr,
                                                   uid,
                                                   goal,
                                                   result,
                                                   context=context))

                    else:
                        _logger.exception(
                            _('Invalid return content from the evaluation of code for definition %s'
                              ) % definition.name)

            else:  # count or sum

                obj = self.pool.get(definition.model_id.model)
                field_date_name = definition.field_date_id and definition.field_date_id.name or False

                if definition.computation_mode == 'count' and definition.batch_mode:
                    # batch mode, trying to do as much as possible in one request
                    general_domain = safe_eval(definition.domain)
                    field_name = definition.batch_distinctive_field.name
                    subqueries = {}
                    for goal in goals:
                        start_date = field_date_name and goal.start_date or False
                        end_date = field_date_name and goal.end_date or False
                        subqueries.setdefault(
                            (start_date, end_date), {}).update({
                                goal.id:
                                safe_eval(definition.batch_user_expression,
                                          {'user': goal.user_id})
                            })

                    # the global query should be split by time periods (especially for recurrent goals)
                    for (start_date,
                         end_date), query_goals in subqueries.items():
                        subquery_domain = list(general_domain)
                        subquery_domain.append(
                            (field_name, 'in',
                             list(set(query_goals.values()))))
                        if start_date:
                            subquery_domain.append(
                                (field_date_name, '>=', start_date))
                        if end_date:
                            subquery_domain.append(
                                (field_date_name, '<=', end_date))

                        if field_name == 'id':
                            # grouping on id does not work and is similar to search anyway
                            user_ids = obj.search(cr,
                                                  uid,
                                                  subquery_domain,
                                                  context=context)
                            user_values = [{
                                'id': user_id,
                                'id_count': 1
                            } for user_id in user_ids]
                        else:
                            user_values = obj.read_group(cr,
                                                         uid,
                                                         subquery_domain,
                                                         fields=[field_name],
                                                         groupby=[field_name],
                                                         context=context)
                        # user_values has format of read_group: [{'partner_id': 42, 'partner_id_count': 3},...]
                        for goal in [
                                g for g in goals if g.id in query_goals.keys()
                        ]:
                            for user_value in user_values:
                                queried_value = field_name in user_value and user_value[
                                    field_name] or False
                                if isinstance(queried_value, tuple) and len(
                                        queried_value) == 2 and isinstance(
                                            queried_value[0], (int, long)):
                                    queried_value = queried_value[0]
                                if queried_value == query_goals[goal.id]:
                                    new_value = user_value.get(
                                        field_name + '_count', goal.current)
                                    goals_to_write.update(
                                        self._get_write_values(
                                            cr,
                                            uid,
                                            goal,
                                            new_value,
                                            context=context))

                else:
                    for goal in goals:
                        # eval the domain with user replaced by goal user object
                        domain = safe_eval(definition.domain,
                                           {'user': goal.user_id})

                        # add temporal clause(s) to the domain if fields are filled on the goal
                        if goal.start_date and field_date_name:
                            domain.append(
                                (field_date_name, '>=', goal.start_date))
                        if goal.end_date and field_date_name:
                            domain.append(
                                (field_date_name, '<=', goal.end_date))

                        if definition.computation_mode == 'sum':
                            field_name = definition.field_id.name
                            # TODO for master: group on user field in batch mode
                            res = obj.read_group(cr,
                                                 uid,
                                                 domain, [field_name], [],
                                                 context=context)
                            new_value = res and res[0][field_name] or 0.0

                        else:  # computation mode = count
                            new_value = obj.search(cr,
                                                   uid,
                                                   domain,
                                                   context=context,
                                                   count=True)

                        goals_to_write.update(
                            self._get_write_values(cr,
                                                   uid,
                                                   goal,
                                                   new_value,
                                                   context=context))

            for goal_id, value in goals_to_write.items():
                if not value:
                    continue
                self.write(cr, uid, [goal_id], value, context=context)
            if commit:
                cr.commit()
        return True

    def action_start(self, cr, uid, ids, context=None):
        """Mark a goal as started.

        This should only be used when creating goals manually (in draft state)"""
        self.write(cr, uid, ids, {'state': 'inprogress'}, context=context)
        return self.update(cr, uid, ids, context=context)

    def action_reach(self, cr, uid, ids, context=None):
        """Mark a goal as reached.

        If the target goal condition is not met, the state will be reset to In
        Progress at the next goal update until the end date."""
        return self.write(cr, uid, ids, {'state': 'reached'}, context=context)

    def action_fail(self, cr, uid, ids, context=None):
        """Set the state of the goal to failed.

        A failed goal will be ignored in future checks."""
        return self.write(cr, uid, ids, {'state': 'failed'}, context=context)

    def action_cancel(self, cr, uid, ids, context=None):
        """Reset the completion after setting a goal as reached or failed.

        This is only the current state, if the date and/or target criterias
        match the conditions for a change of state, this will be applied at the
        next goal update."""
        return self.write(cr,
                          uid,
                          ids, {'state': 'inprogress'},
                          context=context)

    def create(self, cr, uid, vals, context=None):
        """Overwrite the create method to add a 'no_remind_goal' field to True"""
        context = dict(context or {})
        context['no_remind_goal'] = True
        return super(gamification_goal, self).create(cr,
                                                     uid,
                                                     vals,
                                                     context=context)

    def write(self, cr, uid, ids, vals, context=None):
        """Overwrite the write method to update the last_update field to today

        If the current value is changed and the report frequency is set to On
        change, a report is generated
        """
        if context is None:
            context = {}
        vals['last_update'] = fields.date.today()
        result = super(gamification_goal, self).write(cr,
                                                      uid,
                                                      ids,
                                                      vals,
                                                      context=context)
        for goal in self.browse(cr, uid, ids, context=context):
            if goal.state != "draft" and ('definition_id' in vals
                                          or 'user_id' in vals):
                # avoid drag&drop in kanban view
                raise UserError(
                    _('Can not modify the configuration of a started goal'))

            if vals.get('current'):
                if 'no_remind_goal' in context:
                    # new goals should not be reported
                    continue

                if goal.challenge_id and goal.challenge_id.report_message_frequency == 'onchange':
                    self.pool.get('gamification.challenge').report_progress(
                        cr,
                        SUPERUSER_ID,
                        goal.challenge_id,
                        users=[goal.user_id],
                        context=context)
        return result

    def get_action(self, cr, uid, goal_id, context=None):
        """Get the ir.action related to update the goal

        In case of a manual goal, should return a wizard to update the value
        :return: action description in a dictionnary
        """
        goal = self.browse(cr, uid, goal_id, context=context)

        if goal.definition_id.action_id:
            # open a the action linked to the goal
            action = goal.definition_id.action_id.read()[0]

            if goal.definition_id.res_id_field:
                current_user = self.pool.get('res.users').browse(
                    cr, uid, uid, context=context)
                action['res_id'] = safe_eval(goal.definition_id.res_id_field,
                                             {'user': current_user})

                # if one element to display, should see it in form mode if possible
                action['views'] = [(view_id, mode)
                                   for (view_id, mode) in action['views']
                                   if mode == 'form'] or action['views']
            return action

        if goal.computation_mode == 'manually':
            # open a wizard window to update the value manually
            action = {
                'name': _("Update %s") % goal.definition_id.name,
                'id': goal_id,
                'type': 'ir.actions.act_window',
                'views': [[False, 'form']],
                'target': 'new',
                'context': {
                    'default_goal_id': goal_id,
                    'default_current': goal.current
                },
                'res_model': 'gamification.goal.wizard'
            }
            return action

        return False