Ejemplo n.º 1
0
class One2ManyMultiple(orm.Model):
    _name = 'export.one2many.multiple'
    _columns = {
        'parent_id': fields.many2one('export.one2many.recursive'),
        'const': fields.integer(),
        'child1': fields.one2many('export.one2many.child.1', 'parent_id'),
        'child2': fields.one2many('export.one2many.child.2', 'parent_id'),
    }
    _defaults = {
        'const': 36,
    }
Ejemplo n.º 2
0
class pos_config(osv.osv):
    _inherit = 'pos.config'
    _columns = {
        'iface_splitbill':
        fields.boolean('Bill Splitting',
                       help='Enables Bill Splitting in the Point of Sale'),
        'iface_printbill':
        fields.boolean('Bill Printing',
                       help='Allows to print the Bill before payment'),
        'iface_orderline_notes':
        fields.boolean('Orderline Notes',
                       help='Allow custom notes on Orderlines'),
        'floor_ids':
        fields.one2many(
            'restaurant.floor',
            'pos_config_id',
            'Restaurant Floors',
            help='The restaurant floors served by this point of sale'),
        'printer_ids':
        fields.many2many('restaurant.printer',
                         'pos_config_printer_rel',
                         'config_id',
                         'printer_id',
                         string='Order Printers'),
    }
    _defaults = {
        'iface_splitbill': False,
        'iface_printbill': False,
    }
Ejemplo n.º 3
0
class PaymentMethod(osv.Model):
    _name = 'payment.method'
    _order = 'partner_id'

    _columns = {
        'name': fields.char('Name', help='Name of the payment method'),
        'partner_id': fields.many2one('res.partner', 'Partner', required=True),
        'acquirer_id': fields.many2one('payment.acquirer', 'Acquirer Account', required=True),
        'acquirer_ref': fields.char('Acquirer Ref.', required=True),
        'active': fields.boolean('Active'),
        'payment_ids': fields.one2many('payment.transaction', 'payment_method_id', 'Payment Transactions'),
    }

    _defaults = {
        'active': True
    }

    def create(self, cr, uid, values, context=None):
        # call custom create method if defined (i.e. ogone_create for ogone)
        if values.get('acquirer_id'):
            acquirer = self.pool['payment.acquirer'].browse(cr, uid, values.get('acquirer_id'), context=context)

            # custom create
            custom_method_name = '%s_create' % acquirer.provider
            if hasattr(self, custom_method_name):
                values.update(getattr(self, custom_method_name)(cr, uid, values, context=context))

        return super(PaymentMethod, self).create(cr, uid, values, context=context)
Ejemplo n.º 4
0
class product_putaway_strategy(osv.osv):
    _name = 'product.putaway'
    _description = 'Put Away Strategy'

    def _get_putaway_options(self, cr, uid, context=None):
        return [('fixed', 'Fixed Location')]

    _columns = {
        'name':
        fields.char('Name', required=True),
        'method':
        fields.selection(_get_putaway_options, "Method", required=True),
        'fixed_location_ids':
        fields.one2many(
            'stock.fixed.putaway.strat',
            'putaway_id',
            'Fixed Locations Per Product Category',
            help=
            "When the method is fixed, this location will be used to store the products",
            copy=True),
    }

    _defaults = {
        'method': 'fixed',
    }

    def putaway_apply(self, cr, uid, putaway_strat, product, context=None):
        if putaway_strat.method == 'fixed':
            for strat in putaway_strat.fixed_location_ids:
                categ = product.categ_id
                while categ:
                    if strat.category_id.id == categ.id:
                        return strat.fixed_location_id.id
                    categ = categ.parent_id
Ejemplo n.º 5
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"),
    }
Ejemplo n.º 6
0
class mrp_bom(osv.osv):
    _name = 'mrp.bom'
    _description = 'Bill of Material'
    _inherit='mrp.bom'

    _columns={
        'sub_products':fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts', copy=True),
    }
Ejemplo n.º 7
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
Ejemplo n.º 8
0
class crm_team(osv.Model):
    _name = "crm.team"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = "Sales Team"
    _order = "name"
    _period_number = 5

    def _get_default_team_id(self, cr, uid, context=None, user_id=None):
        if context is None:
            context = {}
        if user_id is None:
            user_id = uid
        team_id = context.get('default_team_id')
        if not team_id:
            team_ids = self.search(cr,
                                   uid, [
                                       '|', ('user_id', '=', user_id),
                                       ('member_ids', 'in', user_id)
                                   ],
                                   limit=1,
                                   context=context)
            team_id = team_ids[0] if team_ids else False
        if not team_id:
            team_id = self.pool['ir.model.data'].xmlid_to_res_id(
                cr, uid, 'sales_team.team_sales_department')
        return team_id

    _columns = {
        'name': fields.char('Sales Team', size=64, required=True, translate=True),
        'code': fields.char('Code', size=8),
        'active': fields.boolean('Active', help="If the active field is set to "\
                        "false, it will allow you to hide the sales team without removing it."),
        'company_id': fields.many2one('res.company', 'Company'),
        'user_id': fields.many2one('res.users', 'Team Leader'),
        'member_ids': fields.one2many('res.users', 'sale_team_id', 'Team Members'),
        'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by YuanCloud about cases in this sales team"),
        'working_hours': fields.float('Working Hours', digits=(16, 2)),
        'color': fields.integer('Color Index'),
    }

    _defaults = {
        'active':
        1,
        'company_id':
        lambda self, cr, uid, context: self.pool.get('res.company').
        _company_default_get(cr, uid, 'crm.team', context=context),
    }

    _sql_constraints = [('code_uniq', 'unique (code)',
                         'The code of the sales team must be unique !')]

    def create(self, cr, uid, values, context=None):
        if context is None:
            context = {}
        context['mail_create_nosubscribe'] = True
        return super(crm_team, self).create(cr, uid, values, context=context)
Ejemplo n.º 9
0
class sale_quote_template(osv.osv):
    _name = "sale.quote.template"
    _description = "Sale Quotation Template"
    _columns = {
        'name':
        fields.char('Quotation Template', required=True),
        'website_description':
        fields.html('Description', translate=True),
        'quote_line':
        fields.one2many('sale.quote.line',
                        'quote_id',
                        'Quotation Template Lines',
                        copy=True),
        'note':
        fields.text('Terms and conditions'),
        'options':
        fields.one2many('sale.quote.option',
                        'template_id',
                        'Optional Products Lines',
                        copy=True),
        'number_of_days':
        fields.integer(
            'Quotation Duration',
            help=
            'Number of days for the validity date computation of the quotation'
        ),
        'require_payment':
        fields.selection(
            [(0, 'Not mandatory on website quote validation'),
             (1, 'Immediate after website order validation')],
            'Payment',
            help=
            "Require immediate payment by the customer when validating the order from the website quote"
        ),
    }

    def open_template(self, cr, uid, quote_id, context=None):
        return {
            'type': 'ir.actions.act_url',
            'target': 'self',
            'url': '/quote/template/%d' % quote_id[0]
        }
Ejemplo n.º 10
0
class Documentation(osv.Model):
    _name = 'forum.documentation.toc'
    _description = 'Documentation ToC'
    _inherit = ['website.seo.metadata']
    _order = "parent_left"
    _parent_order = "sequence, name"
    _parent_store = True

    def name_get(self, cr, uid, ids, context=None):
        if isinstance(ids, (list, tuple)) and not len(ids):
            return []
        if isinstance(ids, (long, int)):
            ids = [ids]
        reads = self.read(cr, uid, ids, ['name', 'parent_id'], context=context)
        res = []
        for record in reads:
            name = record['name']
            if record['parent_id']:
                name = record['parent_id'][1]+' / '+name
            res.append((record['id'], name))
        return res

    # TODO master remove me
    def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
        res = self.name_get(cr, uid, ids, context=context)
        return dict(res)

    _columns = {
        'sequence': fields.integer('Sequence'),
        'name': fields.char('Name', required=True, translate=True),
        'introduction': fields.html('Introduction', translate=True),
        'parent_id': fields.many2one('forum.documentation.toc', 'Parent Table Of Content', ondelete='cascade'),
        'child_ids': fields.one2many('forum.documentation.toc', 'parent_id', 'Children Table Of Content'),
        'parent_left': fields.integer('Left Parent', select=True),
        'parent_right': fields.integer('Right Parent', select=True),
        'post_ids': fields.one2many('forum.post', 'documentation_toc_id', 'Posts'),
        'forum_id': fields.many2one('forum.forum', 'Forum', required=True),
    }

    _constraints = [
        (osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
    ]
Ejemplo n.º 11
0
class res_users(osv.Model):
    """ Update of res.users class

     - add field for the related employee of the user
     - if adding groups to an user, check if base.group_user is in it (member of
       'Employee'), create an employee form linked to it. """
    _name = 'res.users'
    _inherit = ['res.users']

    _columns = {
        'employee_ids':
        fields.one2many('hr.employee', 'user_id', 'Related employees'),
    }

    def _message_post_get_eid(self, cr, uid, thread_id, context=None):
        assert thread_id, "res.users does not support posting global messages"
        if context and 'thread_model' in context:
            context = dict(context or {})
            context['thread_model'] = 'hr.employee'
        if isinstance(thread_id, (list, tuple)):
            thread_id = thread_id[0]
        return self.pool.get('hr.employee').search(
            cr, uid, [('user_id', '=', thread_id)], context=context)

    @api.cr_uid_ids_context
    def message_post(self, cr, uid, thread_id, context=None, **kwargs):
        """ Redirect the posting of message on res.users to the related employee.
            This is done because when giving the context of Chatter on the
            various mailboxes, we do not have access to the current partner_id. """
        if kwargs.get('message_type') == 'email':
            return super(res_users, self).message_post(cr,
                                                       uid,
                                                       thread_id,
                                                       context=context,
                                                       **kwargs)
        res = None
        employee_ids = self._message_post_get_eid(cr,
                                                  uid,
                                                  thread_id,
                                                  context=context)
        if not employee_ids:  # no employee: fall back on previous behavior
            return super(res_users, self).message_post(cr,
                                                       uid,
                                                       thread_id,
                                                       context=context,
                                                       **kwargs)
        for employee_id in employee_ids:
            res = self.pool.get('hr.employee').message_post(cr,
                                                            uid,
                                                            employee_id,
                                                            context=context,
                                                            **kwargs)
        return res
Ejemplo n.º 12
0
class product(osv.osv):
    _inherit = 'product.product'
    _columns = {
        'event_ticket_ids':
        fields.one2many('event.event.ticket', 'product_id', 'Event Tickets'),
    }

    def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None):
        """ Redirection, inheritance mechanism hides the method on the model """
        if event_ok:
            return {'value': {'type': 'service'}}
        return {}
Ejemplo n.º 13
0
class sale_order_line(osv.Model):
    _inherit = "sale.order.line"
    _columns = {
        'linked_line_id':
        fields.many2one('sale.order.line',
                        'Linked Order Line',
                        domain="[('order_id','!=',order_id)]",
                        ondelete='cascade'),
        'option_line_ids':
        fields.one2many('sale.order.line',
                        'linked_line_id',
                        string='Options Linked'),
    }
Ejemplo n.º 14
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'),
    }
Ejemplo n.º 15
0
class MailThread(osv.AbstractModel):
    _inherit = 'mail.thread'

    _columns = {
        'website_message_ids':
        fields.one2many(
            'mail.message',
            'res_id',
            domain=lambda self: [
                '&', ('model', '=', self._name),
                ('message_type', '=', 'comment')
            ],
            string='Website Messages',
            help="Website communication history",
        ),
    }
Ejemplo n.º 16
0
class sale_order_line(osv.osv):
    _inherit = "sale.order.line"
    _description = "Sales Order Line"
    _columns = {
        'website_description':
        fields.html('Line Description'),
        'option_line_id':
        fields.one2many('sale.order.option', 'line_id',
                        'Optional Products Lines'),
    }

    def _inject_quote_description(self, cr, uid, values, context=None):
        values = dict(values or {})
        if not values.get('website_description') and values.get('product_id'):
            product = self.pool['product.product'].browse(cr,
                                                          uid,
                                                          values['product_id'],
                                                          context=context)
            values[
                'website_description'] = product.quote_description or product.website_description
        return values

    def create(self, cr, uid, values, context=None):
        values = self._inject_quote_description(cr, uid, values, context)
        ret = super(sale_order_line, self).create(cr,
                                                  uid,
                                                  values,
                                                  context=context)
        # hack because create don t make the job for a related field
        if values.get('website_description'):
            self.write(cr,
                       uid,
                       ret,
                       {'website_description': values['website_description']},
                       context=context)
        return ret

    def write(self, cr, uid, ids, values, context=None):
        values = self._inject_quote_description(cr, uid, values, context)
        return super(sale_order_line, self).write(cr,
                                                  uid,
                                                  ids,
                                                  values,
                                                  context=context)
Ejemplo n.º 17
0
class procurement_group(osv.osv):
    '''
    The procurement group class is used to group products together
    when computing procurements. (tasks, physical products, ...)

    The goal is that when you have one sale order of several products
    and the products are pulled from the same or several location(s), to keep
    having the moves grouped into pickings that represent the sale order.

    Used in: sales order (to group delivery order lines like the so), pull/push
    rules (to pack like the delivery order), on orderpoints (e.g. for wave picking
    all the similar products together).

    Grouping is made only if the source and the destination is the same.
    Suppose you have 4 lines on a picking from Output where 2 lines will need
    to come from Input (crossdock) and 2 lines coming from Stock -> Output As
    the four procurement orders will have the same group ids from the SO, the
    move from input will have a stock.picking with 2 grouped lines and the move
    from stock will have 2 grouped lines also.

    The name is usually the name of the original document (sale order) or a
    sequence computed if created manually.
    '''
    _name = 'procurement.group'
    _description = 'Procurement Requisition'
    _order = "id desc"
    _columns = {
        'name':
        fields.char('Reference', required=True),
        'move_type':
        fields.selection([('direct', 'Partial'), ('one', 'All at once')],
                         'Delivery Method',
                         required=True),
        'procurement_ids':
        fields.one2many('procurement.order', 'group_id', 'Procurements'),
    }
    _defaults = {
        'name':
        lambda self, cr, uid, c: self.pool.get('ir.sequence').next_by_code(
            cr, uid, 'procurement.group') or '',
        'move_type':
        lambda self, cr, uid, c: 'direct'
    }
Ejemplo n.º 18
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
Ejemplo n.º 19
0
class restaurant_floor(osv.osv):
    _name = 'restaurant.floor'
    _columns = {
        'name':
        fields.char('Floor Name',
                    required=True,
                    help='An internal identification of the restaurant floor'),
        'pos_config_id':
        fields.many2one('pos.config', 'Point of Sale'),
        'background_image':
        fields.binary(
            'Background Image',
            attachment=True,
            help=
            'A background image used to display a floor layout in the point of sale interface'
        ),
        'background_color':
        fields.char(
            'Background Color',
            help=
            'The background color of the floor layout, (must be specified in a html-compatible format)'
        ),
        'table_ids':
        fields.one2many('restaurant.table',
                        'floor_id',
                        'Tables',
                        help='The list of tables in this floor'),
        'sequence':
        fields.integer('Sequence', help='Used to sort Floors'),
    }

    _defaults = {
        'sequence': 1,
        'background_color': 'rgb(210, 210, 210)',
    }

    def set_background_color(self, cr, uid, id, background, context=None):
        self.write(cr,
                   uid, [id], {'background_color': background},
                   context=context)
Ejemplo n.º 20
0
class hr_evaluation_plan(osv.Model):
    _name = "hr_evaluation.plan"
    _description = "Appraisal Plan"
    _columns = {
        'name':
        fields.char("Appraisal Plan", required=True),
        'company_id':
        fields.many2one('res.company', 'Company', required=True),
        'phase_ids':
        fields.one2many('hr_evaluation.plan.phase',
                        'plan_id',
                        'Appraisal Phases',
                        copy=True),
        'month_first':
        fields.integer(
            'First Appraisal in (months)',
            help=
            "This number of months will be used to schedule the first evaluation date of the employee when selecting an evaluation plan. "
        ),
        'month_next':
        fields.integer(
            'Periodicity of Appraisal (months)',
            help=
            "The number of month that depicts the delay between each evaluation of this plan (after the first one)."
        ),
        'active':
        fields.boolean('Active')
    }
    _defaults = {
        'active':
        True,
        'month_first':
        6,
        'month_next':
        12,
        'company_id':
        lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
            cr, uid, 'account.account', context=c),
    }
Ejemplo n.º 21
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
Ejemplo n.º 22
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')
    }
Ejemplo n.º 23
0
class subscription_document(osv.osv):
    _name = "subscription.document"
    _description = "Subscription Document"
    _columns = {
        'name':
        fields.char('Name', required=True),
        'active':
        fields.boolean(
            'Active',
            help=
            "If the active field is set to False, it will allow you to hide the subscription document without removing it."
        ),
        'model':
        fields.many2one('ir.model', 'Object', required=True),
        'field_ids':
        fields.one2many('subscription.document.fields',
                        'document_id',
                        'Fields',
                        copy=True)
    }
    _defaults = {
        'active': lambda *a: True,
    }
Ejemplo n.º 24
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
Ejemplo n.º 25
0
class stock_picking_wave(osv.osv):
    _inherit = "mail.thread"
    _name = "stock.picking.wave"
    _description = "Picking Wave"
    _order = "name desc"
    _columns = {
        'name':
        fields.char('Picking Wave Name',
                    required=True,
                    help='Name of the picking wave',
                    copy=False),
        'user_id':
        fields.many2one('res.users',
                        'Responsible',
                        track_visibility='onchange',
                        help='Person responsible for this wave'),
        'picking_ids':
        fields.one2many('stock.picking',
                        'wave_id',
                        'Pickings',
                        help='List of picking associated to this wave'),
        'state':
        fields.selection([('draft', 'Draft'), ('in_progress', 'Running'),
                          ('done', 'Done'), ('cancel', 'Cancelled')],
                         string="State",
                         track_visibility='onchange',
                         required=True,
                         copy=False),
    }

    _defaults = {
        'name': '/',
        'state': 'draft',
    }

    def confirm_picking(self, cr, uid, ids, context=None):
        picking_todo = self.pool.get('stock.picking').search(
            cr, uid, [('wave_id', 'in', ids)], context=context)
        self.write(cr, uid, ids, {'state': 'in_progress'}, context=context)
        return self.pool.get('stock.picking').action_assign(cr,
                                                            uid,
                                                            picking_todo,
                                                            context=context)

    def cancel_picking(self, cr, uid, ids, context=None):
        picking_todo = self.pool.get('stock.picking').search(
            cr, uid, [('wave_id', 'in', ids)], context=context)
        self.pool.get('stock.picking').action_cancel(cr,
                                                     uid,
                                                     picking_todo,
                                                     context=context)
        return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)

    def print_picking(self, cr, uid, ids, context=None):
        '''
        This function print the report for all picking_ids associated to the picking wave
        '''
        context = dict(context or {})
        picking_ids = []
        for wave in self.browse(cr, uid, ids, context=context):
            picking_ids += [picking.id for picking in wave.picking_ids]
        if not picking_ids:
            raise UserError(_('Nothing to print.'))
        context['active_ids'] = picking_ids
        context['active_model'] = 'stock.picking'
        return self.pool.get("report").get_action(cr,
                                                  uid, [],
                                                  'stock.report_picking',
                                                  context=context)

    def create(self, cr, uid, vals, context=None):
        if vals.get('name', '/') == '/':
            vals['name'] = self.pool.get('ir.sequence').next_by_code(
                cr, uid, 'picking.wave') or '/'
        return super(stock_picking_wave, self).create(cr,
                                                      uid,
                                                      vals,
                                                      context=context)

    def done(self, cr, uid, ids, context=None):
        picking_todo = set()
        for wave in self.browse(cr, uid, ids, context=context):
            for picking in wave.picking_ids:
                if picking.state in ('cancel', 'done'):
                    continue
                if picking.state != 'assigned':
                    raise UserError(
                        _('Some pickings are still waiting for goods. Please check or force their availability before setting this wave to done.'
                          ))
                message_body = "<b>%s:</b> %s <a href=#id=%s&view_type=form&model=stock.picking.wave>%s</a>" % (
                    _("Transferred by"), _("Picking Wave"), wave.id, wave.name)
                picking.message_post(body=message_body)
                picking_todo.add(picking.id)
        if picking_todo:
            self.pool.get('stock.picking').action_done(cr,
                                                       uid,
                                                       list(picking_todo),
                                                       context=context)
        return self.write(cr, uid, ids, {'state': 'done'}, context=context)

    def _track_subtype(self, cr, uid, ids, init_values, context=None):
        if 'state' in init_values:
            return 'stock_picking_wave.mt_wave_state'
        return super(stock_picking_wave, self)._track_subtype(cr,
                                                              uid,
                                                              ids,
                                                              init_values,
                                                              context=context)
Ejemplo n.º 26
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
Ejemplo n.º 27
0
class hr_timesheet_sheet(osv.osv):
    _name = "hr_timesheet_sheet.sheet"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _table = 'hr_timesheet_sheet_sheet'
    _order = "id desc"
    _description = "Timesheet"

    def _total(self, cr, uid, ids, name, args, context=None):
        """ Compute the attendances, analytic lines timesheets and differences between them
            for all the days of a timesheet and the current day
        """
        res = dict.fromkeys(
            ids, {
                'total_attendance': 0.0,
                'total_timesheet': 0.0,
                'total_difference': 0.0,
            })

        cr.execute(
            """
            SELECT sheet_id as id,
                   sum(total_attendance) as total_attendance,
                   sum(total_timesheet) as total_timesheet,
                   sum(total_difference) as  total_difference
            FROM hr_timesheet_sheet_sheet_day
            WHERE sheet_id IN %s
            GROUP BY sheet_id
        """, (tuple(ids), ))

        res.update(dict((x.pop('id'), x) for x in cr.dictfetchall()))

        return res

    def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
        ids_signin = self.pool.get('hr.attendance').search(
            cr, uid, [('sheet_id', '=', sheet_id), ('action', '=', 'sign_in')])
        ids_signout = self.pool.get('hr.attendance').search(
            cr, uid, [('sheet_id', '=', sheet_id),
                      ('action', '=', 'sign_out')])

        if len(ids_signin) != len(ids_signout):
            raise UserError(
                _('The timesheet cannot be validated as it does not contain an equal number of sign ins and sign outs.'
                  ))
        return True

    def copy(self, cr, uid, ids, *args, **argv):
        raise UserError(_('You cannot duplicate a timesheet.'))

    def create(self, cr, uid, vals, context=None):
        if 'employee_id' in vals:
            if not self.pool.get('hr.employee').browse(
                    cr, uid, vals['employee_id'], context=context).user_id:
                raise UserError(
                    _('In order to create a timesheet for this employee, you must link him/her to a user.'
                      ))
        if vals.get('attendances_ids'):
            # If attendances, we sort them by date asc before writing them, to satisfy the alternance constraint
            vals['attendances_ids'] = self.sort_attendances(
                cr, uid, vals['attendances_ids'], context=context)
        return super(hr_timesheet_sheet, self).create(cr,
                                                      uid,
                                                      vals,
                                                      context=context)

    def write(self, cr, uid, ids, vals, context=None):
        if 'employee_id' in vals:
            new_user_id = self.pool.get('hr.employee').browse(
                cr, uid, vals['employee_id'],
                context=context).user_id.id or False
            if not new_user_id:
                raise UserError(
                    _('In order to create a timesheet for this employee, you must link him/her to a user.'
                      ))
            if not self._sheet_date(
                    cr, uid, ids, forced_user_id=new_user_id, context=context):
                raise UserError(
                    _('You cannot have 2 timesheets that overlap!\nYou should use the menu \'My Timesheet\' to avoid this problem.'
                      ))
            if not self.pool.get('hr.employee').browse(
                    cr, uid, vals['employee_id'], context=context).product_id:
                raise UserError(
                    _('In order to create a timesheet for this employee, you must link the employee to a product.'
                      ))
        if vals.get('attendances_ids'):
            # If attendances, we sort them by date asc before writing them, to satisfy the alternance constraint
            # In addition to the date order, deleting attendances are done before inserting attendances
            vals['attendances_ids'] = self.sort_attendances(
                cr, uid, vals['attendances_ids'], context=context)
        res = super(hr_timesheet_sheet, self).write(cr,
                                                    uid,
                                                    ids,
                                                    vals,
                                                    context=context)
        if vals.get('attendances_ids'):
            for timesheet in self.browse(cr, uid, ids):
                if not self.pool['hr.attendance']._altern_si_so(
                        cr, uid, [att.id
                                  for att in timesheet.attendances_ids]):
                    raise UserError(
                        _('Error ! Sign in (resp. Sign out) must follow Sign out (resp. Sign in)'
                          ))
        return res

    def sort_attendances(self, cr, uid, attendance_tuples, context=None):
        date_attendances = []
        for att_tuple in attendance_tuples:
            if att_tuple[0] in [0, 1, 4]:
                if att_tuple[0] in [0, 1]:
                    if att_tuple[2] and att_tuple[2].has_key('name'):
                        name = att_tuple[2]['name']
                    else:
                        name = self.pool['hr.attendance'].browse(
                            cr, uid, att_tuple[1]).name
                else:
                    name = self.pool['hr.attendance'].browse(
                        cr, uid, att_tuple[1]).name
                date_attendances.append((1, name, att_tuple))
            elif att_tuple[0] in [2, 3]:
                date_attendances.append((0, self.pool['hr.attendance'].browse(
                    cr, uid, att_tuple[1]).name, att_tuple))
            else:
                date_attendances.append((0, False, att_tuple))
        date_attendances.sort()
        return [att[2] for att in date_attendances]

    def button_confirm(self, cr, uid, ids, context=None):
        for sheet in self.browse(cr, uid, ids, context=context):
            if sheet.employee_id and sheet.employee_id.parent_id and sheet.employee_id.parent_id.user_id:
                self.message_subscribe_users(
                    cr,
                    uid, [sheet.id],
                    user_ids=[sheet.employee_id.parent_id.user_id.id],
                    context=context)
            self.check_employee_attendance_state(cr,
                                                 uid,
                                                 sheet.id,
                                                 context=context)
            di = sheet.user_id.company_id.timesheet_max_difference
            if (abs(sheet.total_difference) < di) or not di:
                sheet.signal_workflow('confirm')
            else:
                raise UserError(
                    _('Please verify that the total difference of the sheet is lower than %.2f.'
                      ) % (di, ))
        return True

    def attendance_action_change(self, cr, uid, ids, context=None):
        hr_employee = self.pool.get('hr.employee')
        employee_ids = []
        for sheet in self.browse(cr, uid, ids, context=context):
            if sheet.employee_id.id not in employee_ids:
                employee_ids.append(sheet.employee_id.id)
        return hr_employee.attendance_action_change(cr,
                                                    uid,
                                                    employee_ids,
                                                    context=context)

    def _count_attendances(self, cr, uid, ids, field_name, arg, context=None):
        res = dict.fromkeys(ids, 0)
        attendances_groups = self.pool['hr.attendance'].read_group(
            cr,
            uid, [('sheet_id', 'in', ids)], ['sheet_id'],
            'sheet_id',
            context=context)
        for attendances in attendances_groups:
            res[attendances['sheet_id'][0]] = attendances['sheet_id_count']
        return res

    _columns = {
        'name':
        fields.char('Note',
                    select=1,
                    states={
                        'confirm': [('readonly', True)],
                        'done': [('readonly', True)]
                    }),
        'employee_id':
        fields.many2one('hr.employee', 'Employee', required=True),
        'user_id':
        fields.related(
            'employee_id',
            'user_id',
            type="many2one",
            relation="res.users",
            store=True,
            string="User",
            required=False,
            readonly=True
        ),  #fields.many2one('res.users', 'User', required=True, select=1, states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}),
        'date_from':
        fields.date('Date from',
                    required=True,
                    select=1,
                    readonly=True,
                    states={'new': [('readonly', False)]}),
        'date_to':
        fields.date('Date to',
                    required=True,
                    select=1,
                    readonly=True,
                    states={'new': [('readonly', False)]}),
        'timesheet_ids':
        fields.one2many('account.analytic.line',
                        'sheet_id',
                        'Timesheet lines',
                        readonly=True,
                        states={
                            'draft': [('readonly', False)],
                            'new': [('readonly', False)]
                        }),
        'attendances_ids':
        fields.one2many('hr.attendance', 'sheet_id', 'Attendances'),
        'state':
        fields.selection(
            [('new', 'New'), ('draft', 'Open'),
             ('confirm', 'Waiting Approval'), ('done', 'Approved')],
            'Status',
            select=True,
            required=True,
            readonly=True,
            track_visibility='onchange',
            help=
            ' * The \'Draft\' status is used when a user is encoding a new and unconfirmed timesheet. \
                \n* The \'Confirmed\' status is used for to confirm the timesheet by user. \
                \n* The \'Done\' status is used when users timesheet is accepted by his/her senior.'
        ),
        'state_attendance':
        fields.related('employee_id',
                       'state',
                       type='selection',
                       selection=[('absent', 'Absent'),
                                  ('present', 'Present')],
                       string='Current Status',
                       readonly=True),
        'total_attendance':
        fields.function(_total,
                        method=True,
                        string='Total Attendance',
                        multi="_total"),
        'total_timesheet':
        fields.function(_total,
                        method=True,
                        string='Total Timesheet',
                        multi="_total"),
        'total_difference':
        fields.function(_total,
                        method=True,
                        string='Difference',
                        multi="_total"),
        'period_ids':
        fields.one2many('hr_timesheet_sheet.sheet.day',
                        'sheet_id',
                        'Period',
                        readonly=True),
        'account_ids':
        fields.one2many('hr_timesheet_sheet.sheet.account',
                        'sheet_id',
                        'Analytic accounts',
                        readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'attendance_count':
        fields.function(_count_attendances,
                        type='integer',
                        string="Attendances"),
    }

    def _default_date_from(self, cr, uid, context=None):
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        r = user.company_id and user.company_id.timesheet_range or 'month'
        if r == 'month':
            return time.strftime('%Y-%m-01')
        elif r == 'week':
            return (datetime.today() +
                    relativedelta(weekday=0, days=-6)).strftime('%Y-%m-%d')
        elif r == 'year':
            return time.strftime('%Y-01-01')
        return fields.date.context_today(self, cr, uid, context)

    def _default_date_to(self, cr, uid, context=None):
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        r = user.company_id and user.company_id.timesheet_range or 'month'
        if r == 'month':
            return (
                datetime.today() +
                relativedelta(months=+1, day=1, days=-1)).strftime('%Y-%m-%d')
        elif r == 'week':
            return (datetime.today() +
                    relativedelta(weekday=6)).strftime('%Y-%m-%d')
        elif r == 'year':
            return time.strftime('%Y-12-31')
        return fields.date.context_today(self, cr, uid, context)

    def _default_employee(self, cr, uid, context=None):
        emp_ids = self.pool.get('hr.employee').search(cr,
                                                      uid,
                                                      [('user_id', '=', uid)],
                                                      context=context)
        return emp_ids and emp_ids[0] or False

    _defaults = {
        'date_from':
        _default_date_from,
        'date_to':
        _default_date_to,
        'state':
        'new',
        'employee_id':
        _default_employee,
        'company_id':
        lambda self, cr, uid, c: self.pool.get('res.company').
        _company_default_get(cr, uid, 'hr_timesheet_sheet.sheet', context=c)
    }

    def _sheet_date(self, cr, uid, ids, forced_user_id=False, context=None):
        for sheet in self.browse(cr, uid, ids, context=context):
            new_user_id = forced_user_id or sheet.employee_id.user_id and sheet.employee_id.user_id.id
            if new_user_id:
                cr.execute(
                    'SELECT id \
                    FROM hr_timesheet_sheet_sheet \
                    WHERE (date_from <= %s and %s <= date_to) \
                        AND user_id=%s \
                        AND id <> %s',
                    (sheet.date_to, sheet.date_from, new_user_id, sheet.id))
                if cr.fetchall():
                    return False
        return True

    _constraints = [
        (_sheet_date,
         'You cannot have 2 timesheets that overlap!\nPlease use the menu \'My Current Timesheet\' to avoid this problem.',
         ['date_from', 'date_to']),
    ]

    def action_set_to_draft(self, cr, uid, ids, *args):
        self.write(cr, uid, ids, {'state': 'draft'})
        self.create_workflow(cr, uid, ids)
        return True

    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        if isinstance(ids, (long, int)):
            ids = [ids]
        # week number according to ISO 8601 Calendar
        return [(r['id'], _('Week ')+str(datetime.strptime(r['date_from'], '%Y-%m-%d').isocalendar()[1])) \
                for r in self.read(cr, uid, ids, ['date_from'],
                    context=context, load='_classic_write')]

    def unlink(self, cr, uid, ids, context=None):
        sheets = self.read(cr,
                           uid,
                           ids, ['state', 'total_attendance'],
                           context=context)
        for sheet in sheets:
            if sheet['state'] in ('confirm', 'done'):
                raise UserError(
                    _('You cannot delete a timesheet which is already confirmed.'
                      ))
            elif sheet['total_attendance'] <> 0.00:
                raise UserError(
                    _('You cannot delete a timesheet which have attendance entries.'
                      ))

        toremove = []
        analytic_timesheet = self.pool.get('account.analytic.line')
        for sheet in self.browse(cr, uid, ids, context=context):
            for timesheet in sheet.timesheet_ids:
                toremove.append(timesheet.id)
        analytic_timesheet.unlink(cr, uid, toremove, context=context)

        return super(hr_timesheet_sheet, self).unlink(cr,
                                                      uid,
                                                      ids,
                                                      context=context)

    def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
        department_id = False
        user_id = False
        if employee_id:
            empl_id = self.pool.get('hr.employee').browse(cr,
                                                          uid,
                                                          employee_id,
                                                          context=context)
            department_id = empl_id.department_id.id
            user_id = empl_id.user_id.id
        return {
            'value': {
                'department_id': department_id,
                'user_id': user_id,
            }
        }

    # ------------------------------------------------
    # OpenChatter methods and notifications
    # ------------------------------------------------

    def _track_subtype(self, cr, uid, ids, init_values, context=None):
        record = self.browse(cr, uid, ids[0], context=context)
        if 'state' in init_values and record.state == 'confirm':
            return 'hr_timesheet_sheet.mt_timesheet_confirmed'
        elif 'state' in init_values and record.state == 'done':
            return 'hr_timesheet_sheet.mt_timesheet_approved'
        return super(hr_timesheet_sheet, self)._track_subtype(cr,
                                                              uid,
                                                              ids,
                                                              init_values,
                                                              context=context)

    def _needaction_domain_get(self, cr, uid, context=None):
        emp_obj = self.pool.get('hr.employee')
        empids = emp_obj.search(cr,
                                uid, [('parent_id.user_id', '=', uid)],
                                context=context)
        if not empids:
            return False
        dom = ['&', ('state', '=', 'confirm'), ('employee_id', 'in', empids)]
        return dom
Ejemplo n.º 28
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
Ejemplo n.º 29
0
class stock_move(osv.osv):
    _inherit = 'stock.move'
    _columns = {
        'move_dest_id_lines':
        fields.one2many('stock.move', 'move_dest_id', 'Children Moves')
    }
Ejemplo n.º 30
0
class purchase_order_line(Model):
    _inherit = "purchase.order.line"

    _columns = {
        'goodie_for_line_id':
        fields.many2one('purchase.order.line',
                        'Goodies for',
                        help='The product linked to this goodie lines'),
        'goodies_line_ids':
        fields.one2many('purchase.order.line',
                        'goodie_for_line_id',
                        'Goodies linked',
                        help=''),
    }

    def write(self, cr, uid, ids, vals, context=None):
        if context is None: context = {}
        #TODO I should apply this only for automatic po need a read only mode
        if context.get("updated_from_op"):
            if not context.get('goodies_create_update'):
                ctx = context.copy()
                ctx['goodies_create_update'] = True
                for line in self.browse(cr, uid, ids, context=None):
                    if line.product_id.is_purchase_goodies(context=ctx):
                        vals[
                            'product_qty'] = self._get_new_qty_for_none_goodies_line(
                                cr,
                                uid,
                                vals['product_qty'],
                                line.product_id.id,
                                line.order_id.id,
                                context=ctx)
                    super(purchase_order_line, self).write(cr,
                                                           uid,
                                                           line.id,
                                                           vals,
                                                           context=ctx)

                    qty_added = vals['product_qty'] - line.product_qty
                    for goodie in line.product_id.supplier_goodies_ids:
                        qty = goodie.get_quantity(qty_added, context=ctx)
                        po_line_for_goodie = False
                        for goodies_line in line.goodies_line_ids:
                            if goodies_line.product_id.id == goodie.linked_product_id.id:
                                po_line_for_goodie = goodies_line
                                break
                        #TODO manage correctly uom
                        print 'po_line_for_goodie', po_line_for_goodie
                        if po_line_for_goodie:
                            po_line_for_goodie.write(
                                {
                                    'product_qty':
                                    po_line_for_goodie.product_qty + qty
                                },
                                context=ctx)
                        else:
                            self.create(cr,
                                        uid,
                                        self._prepare_goodies_line(
                                            cr,
                                            uid,
                                            line.id,
                                            goodie,
                                            qty,
                                            line.order_id,
                                            line.date_planned,
                                            context=ctx),
                                        context=ctx)
                        self.update_none_goodies_line(
                            cr,
                            uid,
                            qty,
                            goodie.linked_product_id.id,
                            line.order_id.id,
                            context=ctx)
                return True
        return super(purchase_order_line, self).write(cr,
                                                      uid,
                                                      ids,
                                                      vals,
                                                      context=context)

    def create(self, cr, uid, vals, context=None):
        if context is None: context = {}
        if not context.get('goodies_create_update'):
            ctx = context.copy()
            ctx['goodies_create_update'] = True
            product_obj = self.pool.get('product.product')
            product = product_obj.browse(cr,
                                         uid,
                                         vals['product_id'],
                                         context=ctx)

            if product.is_purchase_goodies(context=ctx):
                vals['product_qty'] = self._get_new_qty_for_none_goodies_line(
                    cr,
                    uid,
                    vals['product_qty'],
                    vals['product_id'],
                    vals['order_id'],
                    context=ctx)

            line_id = super(purchase_order_line, self).create(cr,
                                                              uid,
                                                              vals,
                                                              context=context)

            order = self.pool.get('purchase.order').browse(cr,
                                                           uid,
                                                           vals['order_id'],
                                                           context=context)
            for goodie in product.supplier_goodies_ids:
                qty = goodie.get_quantity(vals['product_qty'], context=ctx)
                self.create(cr,
                            uid,
                            self._prepare_goodies_line(
                                cr,
                                uid,
                                line_id,
                                goodie,
                                qty,
                                order,
                                vals.get('date_planned'),
                                context=ctx),
                            context=ctx)
                self.update_none_goodies_line(cr,
                                              uid,
                                              qty,
                                              goodie.linked_product_id.id,
                                              order.id,
                                              context=ctx)
            return line_id
        else:
            return super(purchase_order_line, self).create(cr,
                                                           uid,
                                                           vals,
                                                           context=context)

    def _get_new_qty_for_none_goodies_line(self,
                                           cr,
                                           uid,
                                           qty,
                                           product_id,
                                           order_id,
                                           context=None):
        """If we want to buy X more product B we have to check if there is not already goodies
        line that containt this product. If yes we have to reduce the qty to buy by the the total
        of goodies lines
        :params qty float: quantity of product to buy
        :params product_id int: product id
        :params order_id: order id
        :return: the quantity for the none goodies line reduced by the quantity of goodies line
        :rtype: float
        """
        goodies_line_ids = self.search(
            cr,
            uid, [['order_id', '=', order_id], ['product_id', '=', product_id],
                  ['goodie_for_line_id', '!=', False]],
            context=context)
        for goodie_line in self.browse(cr,
                                       uid,
                                       goodies_line_ids,
                                       context=context):
            qty -= goodie_line.product_qty
        if qty < 0:
            qty = 0
        return qty

    def update_none_goodies_line(self,
                                 cr,
                                 uid,
                                 goodies_qty,
                                 product_id,
                                 order_id,
                                 context=None):
        """Update the none line goodies, by this I mean :
        If you sold a product A with a goodies B
        If the scheduler have run a minimal rule for B before running the A rule.
        We have a line for the B product and we should remove the qty added by the goodies
        :params goodies_qty float: quantity of goodies product
        :params product_id int: product id
        :params order_id: order id
        :return: True
        :rtype: Boolean
        """
        product_line_id = self.search(
            cr,
            uid, [['order_id', '=', order_id], ['product_id', '=', product_id],
                  ['goodie_for_line_id', '=', False]],
            context=context)
        if product_line_id:
            product_line = self.browse(cr,
                                       uid,
                                       product_line_id[0],
                                       context=context)
            new_qty = product_line.product_qty - goodies_qty
            if new_qty < 0: new_qty = 0
            product_line.write({'product_qty': new_qty}, context=context)
        return True

    def _prepare_goodies_line(self,
                              cr,
                              uid,
                              line_id,
                              goodie,
                              qty,
                              order,
                              date_planned,
                              context=None):
        """Prepare the purchase order line for goodies
        :params goodies browse_record: browse_record of product_links
        :params qty float: quantity of goodies to buy
        :params order browse_record: purchase order that contain this line
        :params schedule_date str: planned to for receiving the product
        :return: dictionnary of value for creating the purchase order line
        :rtype: dict
        """
        #TODO manage correctly uom
        acc_pos_obj = self.pool.get('account.fiscal.position')
        taxes_ids = goodie.product_id.supplier_taxes_id
        taxes = acc_pos_obj.map_tax(cr, uid,
                                    order.partner_id.property_account_position,
                                    taxes_ids)
        ctx = context.copy()
        #set the partner id in the context in order to have the good name for product
        ctx['partner_id'] = order.partner_id.id
        product = self.pool.get('product.product').browse(
            cr, uid, goodie.linked_product_id.id, context=ctx)
        return {
            'name': ">>>%s" % product.partner_ref,
            'product_qty': qty,
            'product_id': product.id,
            'product_uom': product.uom_po_id.id,
            'price_unit': goodie.cost_price or 0.0,
            'date_planned': date_planned,
            'notes': product.description_purchase,
            'taxes_id': [(6, 0, taxes)],
            'order_id': order.id,
            'goodie_for_line_id': line_id
        }