예제 #1
0
파일: res_company.py 프로젝트: LiberTang0/5
class res_company(osv.Model):
    _name = "res.company"
    _inherit = "res.company"
    _columns = {
        "gengo_private_key":
        fields.text("Gengo Private Key",
                    copy=False,
                    groups="base.group_system"),
        "gengo_public_key":
        fields.text("Gengo Public Key", copy=False, groups="base.group_user"),
        "gengo_comment":
        fields.text(
            "Comments",
            help=
            "This comment will be automatically be enclosed in each an every request sent to Gengo",
            groups="base.group_user"),
        "gengo_auto_approve":
        fields.boolean("Auto Approve Translation ?",
                       help="Jobs are Automatically Approved by Gengo.",
                       groups="base.group_user"),
        "gengo_sandbox":
        fields.boolean(
            "Sandbox Mode",
            help=
            "Check this box if you're using the sandbox mode of Gengo, mainly used for testing purpose."
        ),
    }

    _defaults = {
        "gengo_auto_approve": True,
    }
예제 #2
0
class ir_model_fields_anonymization_history(osv.osv):
    _name = 'ir.model.fields.anonymization.history'
    _order = "date desc"

    _columns = {
        'date':
        fields.datetime('Date', required=True, readonly=True),
        'field_ids':
        fields.many2many('ir.model.fields.anonymization',
                         'anonymized_field_to_history_rel',
                         'field_id',
                         'history_id',
                         'Fields',
                         readonly=True),
        'state':
        fields.selection(selection=ANONYMIZATION_HISTORY_STATE,
                         string='Status',
                         required=True,
                         readonly=True),
        'direction':
        fields.selection(selection=ANONYMIZATION_DIRECTION,
                         string='Direction',
                         size=20,
                         required=True,
                         readonly=True),
        'msg':
        fields.text('Message', readonly=True),
        'filepath':
        fields.char(string='File path', readonly=True),
    }
예제 #3
0
파일: ir_logging.py 프로젝트: LiberTang0/5
class ir_logging(osv.Model):
    _name = 'ir.logging'
    _order = 'id DESC'

    EXCEPTIONS_TYPE = [('client', 'Client'), ('server', 'Server')]

    _columns = {
        'create_date':
        fields.datetime('Create Date', readonly=True),
        'create_uid':
        fields.integer('Uid',
                       readonly=True),  # Integer not m2o is intentionnal
        'name':
        fields.char('Name', required=True),
        'type':
        fields.selection(EXCEPTIONS_TYPE,
                         string='Type',
                         required=True,
                         select=True),
        'dbname':
        fields.char('Database Name', select=True),
        'level':
        fields.char('Level', select=True),
        'message':
        fields.text('Message', required=True),
        'path':
        fields.char('Path', required=True),
        'func':
        fields.char('Function', required=True),
        'line':
        fields.char('Line', required=True),
    }
예제 #4
0
class wizard_price(osv.osv):
    _name = "wizard.price"
    _description = "Compute price wizard"
    _columns = {
        'info_field':
        fields.text('Info', readonly=True),
        'real_time_accounting':
        fields.boolean("Generate accounting entries when real-time"),
        'recursive':
        fields.boolean("Change prices of child BoMs too"),
    }

    def default_get(self, cr, uid, fields, context=None):
        res = super(wizard_price, self).default_get(cr,
                                                    uid,
                                                    fields,
                                                    context=context)
        product_pool = self.pool.get('product.template')
        product_obj = product_pool.browse(cr, uid,
                                          context.get('active_id', False))
        if context is None:
            context = {}
        rec_id = context and context.get('active_id', False)
        assert rec_id, _('Active ID is not set in Context.')
        computed_price = product_pool.compute_price(
            cr,
            uid, [],
            template_ids=[product_obj.id],
            test=True,
            context=context)
        if product_obj.id in computed_price:
            res['info_field'] = "%s: %s" % (product_obj.name,
                                            computed_price[product_obj.id])
        else:
            res['info_field'] = ""
        return res

    def compute_from_bom(self, cr, uid, ids, context=None):
        assert len(ids) == 1
        if context is None:
            context = {}
        model = context.get('active_model')
        if model != 'product.template':
            raise UserError(
                _('This wizard is build for product templates, while you are currently running it from a product variant.'
                  ))
        rec_id = context and context.get('active_id', False)
        assert rec_id, _('Active ID is not set in Context.')
        prod_obj = self.pool.get('product.template')
        res = self.browse(cr, uid, ids, context=context)
        prod = prod_obj.browse(cr, uid, rec_id, context=context)
        prod_obj.compute_price(
            cr,
            uid, [],
            template_ids=[prod.id],
            real_time_accounting=res[0].real_time_accounting,
            recursive=res[0].recursive,
            test=False,
            context=context)
예제 #5
0
파일: website.py 프로젝트: ecoreos/hz
class website_seo_metadata(osv.Model):
    _name = 'website.seo.metadata'
    _description = 'SEO metadata'

    _columns = {
        'website_meta_title':
        fields.char("Website meta title", translate=True),
        'website_meta_description':
        fields.text("Website meta description", translate=True),
        'website_meta_keywords':
        fields.char("Website meta keywords", translate=True),
    }
예제 #6
0
class WebsiteResPartner(osv.Model):
    _name = 'res.partner'
    _inherit = [
        'res.partner', 'website.seo.metadata', 'website.published.mixin'
    ]

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

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

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

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

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

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

    _defaults = {
        'website_private': True,
    }
예제 #7
0
class module_category(osv.osv):
    _name = "ir.module.category"
    _description = "Application"

    def _module_nbr(self, cr, uid, ids, prop, unknow_none, context):
        cr.execute(
            'SELECT category_id, COUNT(*) \
                      FROM ir_module_module \
                     WHERE category_id IN %(ids)s \
                        OR category_id IN (SELECT id \
                                             FROM ir_module_category \
                                            WHERE parent_id IN %(ids)s) \
                     GROUP BY category_id', {'ids': tuple(ids)})
        result = dict(cr.fetchall())
        for id in ids:
            cr.execute('select id from ir_module_category where parent_id=%s',
                       (id, ))
            result[id] = sum([result.get(c, 0) for (c, ) in cr.fetchall()],
                             result.get(id, 0))
        return result

    _columns = {
        'name':
        fields.char("Name", required=True, translate=True, select=True),
        'parent_id':
        fields.many2one('ir.module.category',
                        'Parent Application',
                        select=True),
        'child_ids':
        fields.one2many('ir.module.category', 'parent_id',
                        'Child Applications'),
        'module_nr':
        fields.function(_module_nbr, string='Number of Apps', type='integer'),
        'module_ids':
        fields.one2many('ir.module.module', 'category_id', 'Modules'),
        'description':
        fields.text("Description", translate=True),
        'sequence':
        fields.integer('Sequence'),
        'visible':
        fields.boolean('Visible'),
        'xml_id':
        fields.function(osv.osv.get_external_id,
                        type='char',
                        string="External ID"),
    }
    _order = 'name'

    _defaults = {
        'visible': 1,
    }
예제 #8
0
파일: twitter.py 프로젝트: ecoreos/hz
class WebsiteTwitterTweet(osv.osv):
    _name = "website.twitter.tweet"
    _description = "Twitter Tweets"
    _columns = {
        'website_id': fields.many2one('website', string="Website"),
        'screen_name': fields.char("Screen Name"),
        'tweet': fields.text('Tweets'),

        # Twitter IDs are 64-bit unsigned ints, so we need to store them in
        # unlimited precision NUMERIC columns, which can be done with a
        # float field. Used digits=(0,0) to indicate unlimited.
        # Using VARCHAR would work too but would have sorting problems.
        'tweet_id': fields.float("Tweet ID", digits=(0, 0)),  # Twitter
    }
예제 #9
0
파일: res_country.py 프로젝트: LiberTang0/5
class Country(osv.osv):
    _name = 'res.country'
    _description = 'Country'
    _columns = {
        'name': fields.char('Country Name',
                            help='The full name of the country.', required=True, translate=True),
        'code': fields.char('Country Code', size=2,
                            help='The ISO country code in two chars.\n'
                            'You can use this field for quick search.'),
        'address_format': fields.text('Address Format', help="""You can state here the usual format to use for the \
addresses belonging to this country.\n\nYou can use the python-style string patern with all the field of the address \
(for example, use '%(street)s' to display the field 'street') plus
            \n%(state_name)s: the name of the state
            \n%(state_code)s: the code of the state
            \n%(country_name)s: the name of the country
            \n%(country_code)s: the code of the country"""),
        'currency_id': fields.many2one('res.currency', 'Currency'),
        'image': fields.binary("Image", attachment=True),
        'phone_code': fields.integer('Country Calling Code'),
        'country_group_ids': fields.many2many('res.country.group', 'res_country_res_country_group_rel', 'res_country_id', 'res_country_group_id', string='Country Groups'),
        'state_ids': fields.one2many('res.country.state', 'country_id', string='States'),
    }
    _sql_constraints = [
        ('name_uniq', 'unique (name)',
            'The name of the country must be unique !'),
        ('code_uniq', 'unique (code)',
            'The code of the country must be unique !')
    ]
    _defaults = {
        'address_format': "%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s",
    }
    _order = 'name'

    name_search = location_name_search

    def create(self, cursor, user, vals, context=None):
        if vals.get('code'):
            vals['code'] = vals['code'].upper()
        return super(Country, self).create(cursor, user, vals, context=context)

    def write(self, cursor, user, ids, vals, context=None):
        if vals.get('code'):
            vals['code'] = vals['code'].upper()
        return super(Country, self).write(cursor, user, ids, vals, context=context)

    def get_address_fields(self, cr, uid, ids, context=None):
        res = {}
        for country in self.browse(cr, uid, ids, context=context):
            res[country.id] = re.findall('\((.+?)\)', country.address_format)
        return res
예제 #10
0
class crm_lead_forward_to_partner(osv.TransientModel):
    """ Forward info history to partners. """
    _name = 'crm.lead.channel.interested'
    _columns = {
        'interested': fields.boolean('Interested by this lead'),
        'contacted': fields.boolean('Did you contact the lead?', help="The lead has been contacted"),
        'comment': fields.text('Comment', help="What are the elements that have led to this decision?", required=True),
    }
    _defaults = {
        'interested': lambda self, cr, uid, c: c.get('interested', True),
        'contacted': False,
    }

    def action_confirm(self, cr, uid, ids, context=None):
        wizard = self.browse(cr, uid, ids[0], context=context)
        if wizard.interested and not wizard.contacted:
            raise UserError(_("You must contact the lead before saying that you are interested"))
        lead_obj = self.pool.get('crm.lead')
        lead_obj.check_access_rights(cr, uid, 'write')
        if wizard.interested:
            message = _('<p>I am interested by this lead.</p>')
            values = {}
        else:
            stage = 'stage_portal_lead_recycle'
            if wizard.contacted:
                message = _('<p>I am not interested by this lead. I contacted the lead.</p>')
            else:
                message = _('<p>I am not interested by this lead. I have not contacted the lead.</p>')
            values = {'partner_assigned_id': False}
            user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
            partner_ids = self.pool.get('res.partner').search(cr, SUPERUSER_ID, [('id', 'child_of', user.partner_id.commercial_partner_id.id)], context=context)
            lead_obj.message_unsubscribe(cr, SUPERUSER_ID, context.get('active_ids', []), partner_ids, context=None)
            try:
                stage_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm_partner_assign', stage)[1]
            except ValueError:
                stage_id = False
            if stage_id:
                values.update({'stage_id': stage_id})
        if wizard.comment:
            message += '<p>%s</p>' % wizard.comment
        for active_id in context.get('active_ids', []):
            lead_obj.message_post(cr, uid, active_id, body=message, subtype="mail.mt_comment", context=context)
        if values:
            lead_obj.write(cr, SUPERUSER_ID, context.get('active_ids', []), values)
        if wizard.interested:
            for lead in lead_obj.browse(cr, uid, context.get('active_ids', []), context=context):
                lead_obj.convert_opportunity(cr, SUPERUSER_ID, [lead.id], lead.partner_id and lead.partner_id.id or None, context=None)
        return {
            'type': 'ir.actions.act_window_close',
        }
예제 #11
0
class wizard(osv.osv_memory):
    """
        A wizard to manage the creation/removal of portal users.
    """
    _name = 'portal.wizard'
    _description = 'Portal Access Management'

    _columns = {
        'portal_id': fields.many2one('res.groups', domain=[('is_portal', '=', True)], required=True,
            string='Portal', help="The portal that users can be added in or removed from."),
        'user_ids': fields.one2many('portal.wizard.user', 'wizard_id', string='Users'),
        'welcome_message': fields.text(string='Invitation Message',
            help="This text is included in the email sent to new users of the portal."),
    }

    def _default_portal(self, cr, uid, context):
        portal_ids = self.pool.get('res.groups').search(cr, uid, [('is_portal', '=', True)])
        return portal_ids and portal_ids[0] or False

    _defaults = {
        'portal_id': _default_portal,
    }

    def onchange_portal_id(self, cr, uid, ids, portal_id, context=None):
        # for each partner, determine corresponding portal.wizard.user records
        res_partner = self.pool.get('res.partner')
        partner_ids = context and context.get('active_ids') or []
        contact_ids = set()
        user_changes = []
        for partner in res_partner.browse(cr, SUPERUSER_ID, partner_ids, context):
            for contact in (partner.child_ids or [partner]):
                # make sure that each contact appears at most once in the list
                if contact.id not in contact_ids:
                    contact_ids.add(contact.id)
                    in_portal = False
                    if contact.user_ids:
                        in_portal = portal_id in [g.id for g in contact.user_ids[0].groups_id]
                    user_changes.append((0, 0, {
                        'partner_id': contact.id,
                        'email': contact.email,
                        'in_portal': in_portal,
                    }))
        return {'value': {'user_ids': user_changes}}

    def action_apply(self, cr, uid, ids, context=None):
        wizard = self.browse(cr, uid, ids[0], context)
        portal_user_ids = [user.id for user in wizard.user_ids]
        self.pool.get('portal.wizard.user').action_apply(cr, uid, portal_user_ids, context)
        return {'type': 'ir.actions.act_window_close'}
예제 #12
0
파일: test_models.py 프로젝트: ecoreos/hz
class test_converter(orm.Model):
    _name = 'web_editor.converter.test'

    # disable translation export for those brilliant field labels and values
    _translate = False

    _columns = {
        'char':
        fields.char(),
        'integer':
        fields.integer(),
        'float':
        fields.float(),
        'numeric':
        fields.float(digits=(16, 2)),
        'many2one':
        fields.many2one('web_editor.converter.test.sub'),
        'binary':
        fields.binary(),
        'date':
        fields.date(),
        'datetime':
        fields.datetime(),
        'selection':
        fields.selection([
            (1, "réponse A"),
            (2, "réponse B"),
            (3, "réponse C"),
            (4, "réponse D"),
        ]),
        'selection_str':
        fields.selection(
            [
                ('A', "Qu'il n'est pas arrivé à Toronto"),
                ('B', "Qu'il était supposé arriver à Toronto"),
                ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
                ('D', "La réponse D"),
            ],
            string=
            u"Lorsqu'un pancake prend l'avion à destination de Toronto et "
            u"qu'il fait une escale technique à St Claude, on dit:"),
        'html':
        fields.html(),
        'text':
        fields.text(),
    }
예제 #13
0
파일: badge.py 프로젝트: LiberTang0/5
class gamification_badge_user(osv.Model):
    """User having received a badge"""

    _name = 'gamification.badge.user'
    _description = 'Gamification user badge'
    _order = "create_date desc"
    _rec_name = "badge_name"

    _columns = {
        'user_id': fields.many2one('res.users', string="User", required=True, ondelete="cascade"),
        'sender_id': fields.many2one('res.users', string="Sender", help="The user who has send the badge"),
        'badge_id': fields.many2one('gamification.badge', string='Badge', required=True, ondelete="cascade"),
        'challenge_id': fields.many2one('gamification.challenge', string='Challenge originating', help="If this badge was rewarded through a challenge"),
        'comment': fields.text('Comment'),
        'badge_name': fields.related('badge_id', 'name', type="char", string="Badge Name"),
        'create_date': fields.datetime('Created', readonly=True),
        'create_uid': fields.many2one('res.users', string='Creator', readonly=True),
    }


    def _send_badge(self, cr, uid, ids, context=None):
        """Send a notification to a user for receiving a badge

        Does not verify constrains on badge granting.
        The users are added to the owner_ids (create badge_user if needed)
        The stats counters are incremented
        :param ids: list(int) of badge users that will receive the badge
        """
        res = True
        temp_obj = self.pool.get('mail.template')
        user_obj = self.pool.get('res.users')
        template_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'gamification', 'email_template_badge_received')[1]
        for badge_user in self.browse(cr, uid, ids, context=context):
            template = temp_obj.get_email_template(cr, uid, template_id, badge_user.id, context=context)
            body_html = temp_obj.render_template(cr, uid, template.body_html, 'gamification.badge.user', badge_user.id, context=template._context)
            res = user_obj.message_post(
                cr, uid, badge_user.user_id.id,
                body=body_html,
                subtype='gamification.mt_badge_granted',
                partner_ids=[badge_user.user_id.partner_id.id],
                context=context)
        return res

    def create(self, cr, uid, vals, context=None):
        self.pool.get('gamification.badge').check_granting(cr, uid, badge_id=vals.get('badge_id'), context=context)
        return super(gamification_badge_user, self).create(cr, uid, vals, context=context)
예제 #14
0
class ir_model_fields_anonymization_migration_fix(osv.osv):
    _name = 'ir.model.fields.anonymization.migration.fix'
    _order = "sequence"

    _columns = {
        'target_version':
        fields.char('Target Version'),
        'model_name':
        fields.char('Model'),
        'field_name':
        fields.char('Field'),
        'query':
        fields.text('Query'),
        'query_type':
        fields.selection(string='Query',
                         selection=[('sql', 'sql'), ('python', 'python')]),
        'sequence':
        fields.integer('Sequence'),
    }
예제 #15
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]
        }
예제 #16
0
class grant_badge_wizard(osv.TransientModel):
    """ Wizard allowing to grant a badge to a user"""

    _name = 'gamification.badge.user.wizard'
    _columns = {
        'user_id':
        fields.many2one("res.users", string='User', required=True),
        'badge_id':
        fields.many2one("gamification.badge", string='Badge', required=True),
        'comment':
        fields.text('Comment'),
    }

    def action_grant_badge(self, cr, uid, ids, context=None):
        """Wizard action for sending a badge to a chosen user"""

        badge_user_obj = self.pool.get('gamification.badge.user')

        for wiz in self.browse(cr, uid, ids, context=context):
            if uid == wiz.user_id.id:
                raise UserError(_('You can not grant a badge to yourself'))

            #create the badge
            values = {
                'user_id': wiz.user_id.id,
                'sender_id': uid,
                'badge_id': wiz.badge_id.id,
                'comment': wiz.comment,
            }
            badge_user = badge_user_obj.create(cr,
                                               uid,
                                               values,
                                               context=context)
            result = badge_user_obj._send_badge(cr,
                                                uid,
                                                badge_user,
                                                context=context)

        return result
예제 #17
0
class hr_job(osv.Model):
    def _get_nbr_employees(self, cr, uid, ids, name, args, context=None):
        res = {}
        for job in self.browse(cr, uid, ids, context=context):
            nb_employees = len(job.employee_ids or [])
            res[job.id] = {
                'no_of_employee': nb_employees,
                'expected_employees': nb_employees + job.no_of_recruitment,
            }
        return res

    def _get_job_position(self, cr, uid, ids, context=None):
        res = []
        for employee in self.pool.get('hr.employee').browse(cr,
                                                            uid,
                                                            ids,
                                                            context=context):
            if employee.job_id:
                res.append(employee.job_id.id)
        return res

    _name = "hr.job"
    _description = "Job Position"
    _inherit = ['mail.thread']
    _columns = {
        'name':
        fields.char('Job Name', required=True, select=True, translate=True),
        'expected_employees':
        fields.function(
            _get_nbr_employees,
            string='Total Forecasted Employees',
            help=
            'Expected number of employees for this job position after new recruitment.',
            store={
                'hr.job': (lambda self, cr, uid, ids, c=None: ids,
                           ['no_of_recruitment'], 10),
                'hr.employee': (_get_job_position, ['job_id'], 10),
            },
            type='integer',
            multi='_get_nbr_employees'),
        'no_of_employee':
        fields.function(
            _get_nbr_employees,
            string="Current Number of Employees",
            help='Number of employees currently occupying this job position.',
            store={
                'hr.employee': (_get_job_position, ['job_id'], 10),
            },
            type='integer',
            multi='_get_nbr_employees'),
        'no_of_recruitment':
        fields.integer('Expected New Employees',
                       copy=False,
                       help='Number of new employees you expect to recruit.'),
        'no_of_hired_employee':
        fields.integer(
            'Hired Employees',
            copy=False,
            help=
            'Number of hired employees for this job position during recruitment phase.'
        ),
        'employee_ids':
        fields.one2many('hr.employee',
                        'job_id',
                        'Employees',
                        groups='base.group_user'),
        'description':
        fields.text('Job Description'),
        'requirements':
        fields.text('Requirements'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'company_id':
        fields.many2one('res.company', 'Company'),
        'state':
        fields.selection(
            [('recruit', 'Recruitment in Progress'),
             ('open', 'Recruitment Closed')],
            string='Status',
            readonly=True,
            required=True,
            track_visibility='always',
            copy=False,
            help=
            "Set whether the recruitment process is open or closed for this job position."
        ),
        'write_date':
        fields.datetime('Update Date', readonly=True),
    }

    _defaults = {
        'company_id':
        lambda self, cr, uid, ctx=None: self.pool.get('res.company').
        _company_default_get(cr, uid, 'hr.job', context=ctx),
        'state':
        'recruit',
        'no_of_recruitment':
        1,
    }

    _sql_constraints = [
        ('name_company_uniq', 'unique(name, company_id, department_id)',
         'The name of the job position must be unique per department in company!'
         ),
    ]

    def set_recruit(self, cr, uid, ids, context=None):
        for job in self.browse(cr, uid, ids, context=context):
            no_of_recruitment = job.no_of_recruitment == 0 and 1 or job.no_of_recruitment
            self.write(cr,
                       uid, [job.id], {
                           'state': 'recruit',
                           'no_of_recruitment': no_of_recruitment
                       },
                       context=context)
        return True

    def set_open(self, cr, uid, ids, context=None):
        self.write(cr,
                   uid,
                   ids, {
                       'state': 'open',
                       'no_of_recruitment': 0,
                       'no_of_hired_employee': 0
                   },
                   context=context)
        return True

    # TDE note: done in new api, because called with new api -> context is a
    # frozendict -> error when tryign to manipulate it
    @api.model
    def create(self, values):
        return super(
            hr_job,
            self.with_context(mail_create_nosubscribe=True)).create(values)

    def copy(self, cr, uid, id, default=None, context=None):
        if default is None:
            default = {}
        if 'name' not in default:
            job = self.browse(cr, uid, id, context=context)
            default['name'] = _("%s (copy)") % (job.name)
        return super(hr_job, self).copy(cr,
                                        uid,
                                        id,
                                        default=default,
                                        context=context)

    # ----------------------------------------
    # Compatibility methods
    # ----------------------------------------
    _no_of_employee = _get_nbr_employees  # v7 compatibility
    job_open = set_open  # v7 compatibility
    job_recruitment = set_recruit  # v7 compatibility
예제 #18
0
class hr_employee(osv.osv):
    _name = "hr.employee"
    _description = "Employee"
    _order = 'name_related'
    _inherits = {'resource.resource': "resource_id"}
    _inherit = ['mail.thread']

    _mail_post_access = 'read'

    _columns = {
        #we need a related field in order to be able to sort the employee by name
        'name_related':
        fields.related('resource_id',
                       'name',
                       type='char',
                       string='Name',
                       readonly=True,
                       store=True),
        'country_id':
        fields.many2one('res.country', 'Nationality (Country)'),
        'birthday':
        fields.date("Date of Birth"),
        'ssnid':
        fields.char('SSN No', help='Social Security Number'),
        'sinid':
        fields.char('SIN No', help="Social Insurance Number"),
        'identification_id':
        fields.char('Identification No'),
        'gender':
        fields.selection([('male', 'Male'), ('female', 'Female'),
                          ('other', 'Other')], 'Gender'),
        'marital':
        fields.selection([('single', 'Single'), ('married', 'Married'),
                          ('widower', 'Widower'), ('divorced', 'Divorced')],
                         'Marital Status'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'address_id':
        fields.many2one('res.partner', 'Working Address'),
        'address_home_id':
        fields.many2one('res.partner', 'Home Address'),
        'bank_account_id':
        fields.many2one('res.partner.bank',
                        'Bank Account Number',
                        domain="[('partner_id','=',address_home_id)]",
                        help="Employee bank salary account"),
        'work_phone':
        fields.char('Work Phone', readonly=False),
        'mobile_phone':
        fields.char('Work Mobile', readonly=False),
        'work_email':
        fields.char('Work Email', size=240),
        'work_location':
        fields.char('Work Location'),
        'notes':
        fields.text('Notes'),
        'parent_id':
        fields.many2one('hr.employee', 'Manager'),
        'category_ids':
        fields.many2many('hr.employee.category', 'employee_category_rel',
                         'emp_id', 'category_id', 'Tags'),
        'child_ids':
        fields.one2many('hr.employee', 'parent_id', 'Subordinates'),
        'resource_id':
        fields.many2one('resource.resource',
                        'Resource',
                        ondelete='cascade',
                        required=True,
                        auto_join=True),
        'coach_id':
        fields.many2one('hr.employee', 'Coach'),
        'job_id':
        fields.many2one('hr.job', 'Job Title'),
        'passport_id':
        fields.char('Passport No'),
        'color':
        fields.integer('Color Index'),
        'city':
        fields.related('address_id', 'city', type='char', string='City'),
        'login':
        fields.related('user_id',
                       'login',
                       type='char',
                       string='Login',
                       readonly=1),
        'last_login':
        fields.related('user_id',
                       'date',
                       type='datetime',
                       string='Latest Connection',
                       readonly=1),
    }

    # image: all image fields are base64 encoded and PIL-supported
    image = ecore.fields.Binary(
        "Photo",
        attachment=True,
        help=
        "This field holds the image used as photo for the employee, limited to 1024x1024px."
    )
    image_medium = ecore.fields.Binary("Medium-sized photo",
        compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
        help="Medium-sized photo of the employee. It is automatically "\
             "resized as a 128x128px image, with aspect ratio preserved. "\
             "Use this field in form views or some kanban views.")
    image_small = ecore.fields.Binary("Small-sized photo",
        compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
        help="Small-sized photo of the employee. It is automatically "\
             "resized as a 64x64px image, with aspect ratio preserved. "\
             "Use this field anywhere a small image is required.")

    @api.depends('image')
    def _compute_images(self):
        for rec in self:
            rec.image_medium = tools.image_resize_image_medium(rec.image)
            rec.image_small = tools.image_resize_image_small(rec.image)

    def _inverse_image_medium(self):
        for rec in self:
            rec.image = tools.image_resize_image_big(rec.image_medium)

    def _inverse_image_small(self):
        for rec in self:
            rec.image = tools.image_resize_image_big(rec.image_small)

    def _get_default_image(self, cr, uid, context=None):
        image_path = get_module_resource('hr', 'static/src/img',
                                         'default_image.png')
        return tools.image_resize_image_big(
            open(image_path, 'rb').read().encode('base64'))

    defaults = {
        'active': 1,
        'image': _get_default_image,
        'color': 0,
    }

    def unlink(self, cr, uid, ids, context=None):
        resource_ids = []
        for employee in self.browse(cr, uid, ids, context=context):
            resource_ids.append(employee.resource_id.id)
        super(hr_employee, self).unlink(cr, uid, ids, context=context)
        return self.pool.get('resource.resource').unlink(cr,
                                                         uid,
                                                         resource_ids,
                                                         context=context)

    def onchange_address_id(self, cr, uid, ids, address, context=None):
        if address:
            address = self.pool.get('res.partner').browse(cr,
                                                          uid,
                                                          address,
                                                          context=context)
            return {
                'value': {
                    'work_phone': address.phone,
                    'mobile_phone': address.mobile
                }
            }
        return {'value': {}}

    def onchange_company(self, cr, uid, ids, company, context=None):
        address_id = False
        if company:
            company_id = self.pool.get('res.company').browse(cr,
                                                             uid,
                                                             company,
                                                             context=context)
            address = self.pool.get('res.partner').address_get(
                cr, uid, [company_id.partner_id.id], ['contact'])
            address_id = address and address['contact'] or False
        return {'value': {'address_id': address_id}}

    def onchange_department_id(self,
                               cr,
                               uid,
                               ids,
                               department_id,
                               context=None):
        value = {'parent_id': False}
        if department_id:
            department = self.pool.get('hr.department').browse(
                cr, uid, department_id)
            value['parent_id'] = department.manager_id.id
        return {'value': value}

    def onchange_user(self, cr, uid, ids, name, image, user_id, context=None):
        if user_id:
            user = self.pool['res.users'].browse(cr,
                                                 uid,
                                                 user_id,
                                                 context=context)
            values = {
                'name': name or user.name,
                'work_email': user.email,
                'image': image or user.image,
            }
            return {'value': values}

    def action_follow(self, cr, uid, ids, context=None):
        """ Wrapper because message_subscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_subscribe_users(cr, uid, ids, context=context)

    def action_unfollow(self, cr, uid, ids, context=None):
        """ Wrapper because message_unsubscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_unsubscribe_users(cr, uid, ids, context=context)

    def _message_get_auto_subscribe_fields(self,
                                           cr,
                                           uid,
                                           updated_fields,
                                           auto_follow_fields=None,
                                           context=None):
        """ Overwrite of the original method to always follow user_id field,
        even when not track_visibility so that a user will follow it's employee
        """
        if auto_follow_fields is None:
            auto_follow_fields = ['user_id']
        user_field_lst = []
        for name, field in self._fields.items():
            if name in auto_follow_fields and name in updated_fields and field.comodel_name == 'res.users':
                user_field_lst.append(name)
        return user_field_lst

    _constraints = [
        (osv.osv._check_recursion,
         _('Error! You cannot create recursive hierarchy of Employee(s).'),
         ['parent_id']),
    ]
예제 #19
0
class product_template(osv.osv):
    _name = 'product.template'
    _inherit = 'product.template'

    def _product_available(self, cr, uid, ids, name, arg, context=None):
        prod_available = {}
        product_ids = self.browse(cr, uid, ids, context=context)
        var_ids = []
        for product in product_ids:
            var_ids += [p.id for p in product.product_variant_ids]
        variant_available = self.pool['product.product']._product_available(
            cr, uid, var_ids, context=context)

        for product in product_ids:
            qty_available = 0
            virtual_available = 0
            incoming_qty = 0
            outgoing_qty = 0
            for p in product.product_variant_ids:
                qty_available += variant_available[p.id]["qty_available"]
                virtual_available += variant_available[
                    p.id]["virtual_available"]
                incoming_qty += variant_available[p.id]["incoming_qty"]
                outgoing_qty += variant_available[p.id]["outgoing_qty"]
            prod_available[product.id] = {
                "qty_available": qty_available,
                "virtual_available": virtual_available,
                "incoming_qty": incoming_qty,
                "outgoing_qty": outgoing_qty,
            }
        return prod_available

    def _search_product_quantity(self, cr, uid, obj, name, domain, context):
        prod = self.pool.get("product.product")
        product_variant_ids = prod.search(cr, uid, domain, context=context)
        return [('product_variant_ids', 'in', product_variant_ids)]

    def _product_available_text(self,
                                cr,
                                uid,
                                ids,
                                field_names=None,
                                arg=False,
                                context=None):
        res = {}
        for product in self.browse(cr, uid, ids, context=context):
            res[product.id] = str(product.qty_available) + _(" On Hand")
        return res

    def _compute_nbr_reordering_rules(self,
                                      cr,
                                      uid,
                                      ids,
                                      field_names=None,
                                      arg=None,
                                      context=None):
        res = dict.fromkeys(
            ids, {
                'nbr_reordering_rules': 0,
                'reordering_min_qty': 0,
                'reordering_max_qty': 0
            })
        product_data = self.pool['stock.warehouse.orderpoint'].read_group(
            cr,
            uid, [('product_id.product_tmpl_id', 'in', ids)],
            ['product_id', 'product_min_qty', 'product_max_qty'],
            ['product_id'],
            context=context)
        for data in product_data:
            product_tmpl_id = data['__domain'][1][2][0]
            res[product_tmpl_id][
                'nbr_reordering_rules'] = res[product_tmpl_id].get(
                    'nbr_reordering_rules', 0) + int(data['product_id_count'])
            res[product_tmpl_id]['reordering_min_qty'] = data[
                'product_min_qty']
            res[product_tmpl_id]['reordering_max_qty'] = data[
                'product_max_qty']
        return res

    def _get_product_template_type(self, cr, uid, context=None):
        res = super(product_template,
                    self)._get_product_template_type(cr, uid, context=context)
        if 'product' not in [item[0] for item in res]:
            res.append(('product', 'Producto Almacenable'))
        return res

    _columns = {
        'property_stock_procurement':
        fields.property(
            type='many2one',
            relation='stock.location',
            string="Procurement Location",
            domain=[('usage', 'like', 'procurement')],
            help=
            "This stock location will be used, instead of the default one, as the source location for stock moves generated by procurements."
        ),
        'property_stock_production':
        fields.property(
            type='many2one',
            relation='stock.location',
            string="Production Location",
            domain=[('usage', 'like', 'production')],
            help=
            "This stock location will be used, instead of the default one, as the source location for stock moves generated by manufacturing orders."
        ),
        'property_stock_inventory':
        fields.property(
            type='many2one',
            relation='stock.location',
            string="Inventory Location",
            domain=[('usage', 'like', 'inventory')],
            help=
            "This stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory."
        ),
        'sale_delay':
        fields.float(
            'Customer Lead Time',
            help=
            "The average delay in days between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers."
        ),
        'tracking':
        fields.selection(selection=[('serial', 'By Unique Serial Number'),
                                    ('lot', 'By Lots'),
                                    ('none', 'No Tracking')],
                         string="Tracking",
                         required=True),
        'description_picking':
        fields.text('Description on Picking', translate=True),
        # sum of product variant qty
        # 'reception_count': fields.function(_product_available, multi='qty_available',
        #     fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
        # 'delivery_count': fields.function(_product_available, multi='qty_available',
        #     fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
        'qty_available':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Quantity On Hand'),
        'virtual_available':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Forecasted Quantity'),
        'incoming_qty':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Incoming'),
        'outgoing_qty':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Outgoing'),
        'location_id':
        fields.dummy(string='Location',
                     relation='stock.location',
                     type='many2one'),
        'warehouse_id':
        fields.dummy(string='Warehouse',
                     relation='stock.warehouse',
                     type='many2one'),
        'route_ids':
        fields.many2many(
            'stock.location.route',
            'stock_route_product',
            'product_id',
            'route_id',
            'Routes',
            domain="[('product_selectable', '=', True)]",
            help=
            "Depending on the modules installed, this will allow you to define the route of the product: whether it will be bought, manufactured, MTO/MTS,..."
        ),
        'nbr_reordering_rules':
        fields.function(_compute_nbr_reordering_rules,
                        string='Reordering Rules',
                        type='integer',
                        multi=True),
        'reordering_min_qty':
        fields.function(_compute_nbr_reordering_rules,
                        type='float',
                        multi=True),
        'reordering_max_qty':
        fields.function(_compute_nbr_reordering_rules,
                        type='float',
                        multi=True),
        'route_from_categ_ids':
        fields.related('categ_id',
                       'total_route_ids',
                       type="many2many",
                       relation="stock.location.route",
                       string="Category Routes"),
    }

    _defaults = {
        'sale_delay': 7,
        'tracking': 'none',
    }

    def action_view_routes(self, cr, uid, ids, context=None):
        route_obj = self.pool.get("stock.location.route")
        act_obj = self.pool.get('ir.actions.act_window')
        mod_obj = self.pool.get('ir.model.data')
        product_route_ids = set()
        for product in self.browse(cr, uid, ids, context=context):
            product_route_ids |= set([r.id for r in product.route_ids])
            product_route_ids |= set(
                [r.id for r in product.categ_id.total_route_ids])
        route_ids = route_obj.search(cr,
                                     uid, [
                                         '|',
                                         ('id', 'in', list(product_route_ids)),
                                         ('warehouse_selectable', '=', True)
                                     ],
                                     context=context)
        result = mod_obj.xmlid_to_res_id(cr,
                                         uid,
                                         'stock.action_routes_form',
                                         raise_if_not_found=True)
        result = act_obj.read(cr, uid, [result], context=context)[0]
        result['domain'] = "[('id','in',[" + ','.join(map(str,
                                                          route_ids)) + "])]"
        return result

    def onchange_tracking(self, cr, uid, ids, tracking, context=None):
        if not tracking:
            return {}
        product_product = self.pool['product.product']
        variant_ids = product_product.search(cr,
                                             uid,
                                             [('product_tmpl_id', 'in', ids)],
                                             context=context)
        return product_product.onchange_tracking(cr,
                                                 uid,
                                                 variant_ids,
                                                 tracking,
                                                 context=context)

    def _get_products(self, cr, uid, ids, context=None):
        products = []
        for prodtmpl in self.browse(cr, uid, ids, context=None):
            products += [x.id for x in prodtmpl.product_variant_ids]
        return products

    def _get_act_window_dict(self, cr, uid, name, context=None):
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result = mod_obj.xmlid_to_res_id(cr,
                                         uid,
                                         name,
                                         raise_if_not_found=True)
        result = act_obj.read(cr, uid, [result], context=context)[0]
        return result

    def action_open_quants(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr,
                                           uid,
                                           'stock.product_open_quants',
                                           context=context)
        result['domain'] = "[('product_id','in',[" + ','.join(
            map(str, products)) + "])]"
        result[
            'context'] = "{'search_default_locationgroup': 1, 'search_default_internal_loc': 1}"
        return result

    def action_view_orderpoints(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr,
                                           uid,
                                           'stock.product_open_orderpoint',
                                           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

    def action_view_stock_moves(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr,
                                           uid,
                                           'stock.act_product_stock_move_open',
                                           context=context)
        if products:
            result['context'] = "{'default_product_id': %d}" % products[0]
        result['domain'] = "[('product_id.product_tmpl_id','in',[" + ','.join(
            map(str, ids)) + "])]"
        return result

    def write(self, cr, uid, ids, vals, context=None):
        if 'uom_id' in vals:
            new_uom = self.pool.get('product.uom').browse(cr,
                                                          uid,
                                                          vals['uom_id'],
                                                          context=context)
            for product in self.browse(cr, uid, ids, context=context):
                old_uom = product.uom_id
                if old_uom != new_uom:
                    if self.pool.get('stock.move').search(
                            cr,
                            uid,
                        [('product_id', 'in',
                          [x.id for x in product.product_variant_ids]),
                         ('state', '=', 'done')],
                            limit=1,
                            context=context):
                        raise UserError(
                            _("You can not change the unit of measure of a product that has already been used in a done stock move. If you need to change the unit of measure, you may deactivate this product."
                              ))
        return super(product_template, self).write(cr,
                                                   uid,
                                                   ids,
                                                   vals,
                                                   context=context)
예제 #20
0
class ir_translation(osv.osv):
    _name = "ir.translation"
    _log_access = False

    def _get_language(self, cr, uid, context):
        lang_model = self.pool.get('res.lang')
        lang_ids = lang_model.search(cr,
                                     uid, [('translatable', '=', True)],
                                     context=context)
        lang_data = lang_model.read(cr,
                                    uid,
                                    lang_ids, ['code', 'name'],
                                    context=context)
        return [(d['code'], d['name']) for d in lang_data]

    def _get_src(self, cr, uid, ids, name, arg, context=None):
        ''' Get source name for the translation. If object type is model then
        return the value store in db. Otherwise return value store in src field
        '''
        if context is None:
            context = {}
        res = dict.fromkeys(ids, False)
        for record in self.browse(cr, uid, ids, context=context):
            res[record.id] = record.src
            if record.type == 'model':
                model_name, field_name = record.name.split(',')
                model = self.pool.get(model_name)
                if model is None:
                    continue
                field = model._fields.get(field_name)
                if field is None:
                    continue
                if not callable(field.translate):
                    # Pass context without lang, need to read real stored field, not translation
                    context_no_lang = dict(context, lang=None)
                    result = model.read(cr,
                                        uid, [record.res_id], [field_name],
                                        context=context_no_lang)
                    res[record.id] = result[0][field_name] if result else False
        return res

    def _set_src(self, cr, uid, id, name, value, args, context=None):
        ''' When changing source term of a translation, change its value in db for
        the associated object, and the src field
        '''
        if context is None:
            context = {}
        record = self.browse(cr, uid, id, context=context)
        if record.type == 'model':
            model_name, field_name = record.name.split(',')
            model = self.pool.get(model_name)
            field = model._fields[field_name]
            if not callable(field.translate):
                # Make a context without language information, because we want
                # to write on the value stored in db and not on the one
                # associated with the current language. Also not removing lang
                # from context trigger an error when lang is different.
                context_wo_lang = context.copy()
                context_wo_lang.pop('lang', None)
                model.write(cr,
                            uid, [record.res_id], {field_name: value},
                            context=context_wo_lang)
        return self.write(cr, uid, id, {'src': value}, context=context)

    def _search_src(self, cr, uid, obj, name, args, context):
        ''' the source term is stored on 'src' field '''
        res = []
        for field, operator, value in args:
            res.append(('src', operator, value))
        return res

    _columns = {
        'name':
        fields.char('Translated field', required=True),
        'res_id':
        fields.integer('Record ID', select=True),
        'lang':
        fields.selection(_get_language, string='Language'),
        'type':
        fields.selection(TRANSLATION_TYPE, string='Type', select=True),
        'src':
        fields.text('Internal Source'
                    ),  # stored in database, kept for backward compatibility
        'source':
        fields.function(_get_src,
                        fnct_inv=_set_src,
                        fnct_search=_search_src,
                        type='text',
                        string='Source term'),
        'value':
        fields.text('Translation Value'),
        'module':
        fields.char('Module', help="Module this term belongs to", select=True),
        'state':
        fields.selection(
            [('to_translate', 'To Translate'),
             ('inprogress', 'Translation in Progress'),
             ('translated', 'Translated')],
            string="Status",
            help=
            "Automatically set to let administators find new terms that might need to be translated"
        ),

        # aka gettext extracted-comments - we use them to flag ecore-web translation
        # cfr: http://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/PO-Files.html
        'comments':
        fields.text('Translation comments', select=True),
    }

    _defaults = {
        'state': 'to_translate',
    }

    _sql_constraints = [
        ('lang_fkey_res_lang', 'FOREIGN KEY(lang) REFERENCES res_lang(code)',
         'Language code of translation item must be among known languages'),
    ]

    def _auto_init(self, cr, context=None):
        super(ir_translation, self)._auto_init(cr, context)

        cr.execute(
            "SELECT indexname FROM pg_indexes WHERE indexname LIKE 'ir_translation_%'"
        )
        indexes = [row[0] for row in cr.fetchall()]

        # Removed because there is a size limit on btree indexed values (problem with column src):
        # cr.execute('CREATE INDEX ir_translation_ltns ON ir_translation (name, lang, type, src)')
        # cr.execute('CREATE INDEX ir_translation_lts ON ir_translation (lang, type, src)')
        #
        # Removed because hash indexes are not compatible with postgres streaming replication:
        # cr.execute('CREATE INDEX ir_translation_src_hash_idx ON ir_translation USING hash (src)')
        if set(indexes) & set([
                'ir_translation_ltns', 'ir_translation_lts',
                'ir_translation_src_hash_idx'
        ]):
            cr.execute(
                'DROP INDEX IF EXISTS ir_translation_ltns, ir_translation_lts, ir_translation_src_hash_idx'
            )
            cr.commit()

        # Add separate md5 index on src (no size limit on values, and good performance).
        if 'ir_translation_src_md5' not in indexes:
            cr.execute(
                'CREATE INDEX ir_translation_src_md5 ON ir_translation (md5(src))'
            )
            cr.commit()

        if 'ir_translation_ltn' not in indexes:
            cr.execute(
                'CREATE INDEX ir_translation_ltn ON ir_translation (name, lang, type)'
            )
            cr.commit()

    def _check_selection_field_value(self,
                                     cr,
                                     uid,
                                     field,
                                     value,
                                     context=None):
        if field == 'lang':
            return
        return super(ir_translation,
                     self)._check_selection_field_value(cr,
                                                        uid,
                                                        field,
                                                        value,
                                                        context=context)

    def _get_ids(self, cr, uid, name, tt, lang, ids):
        translations = dict.fromkeys(ids, False)
        if ids:
            cr.execute(
                'select res_id,value '
                'from ir_translation '
                'where lang=%s '
                'and type=%s '
                'and name=%s '
                'and res_id IN %s', (lang, tt, name, tuple(ids)))
            for res_id, value in cr.fetchall():
                translations[res_id] = value
        return translations

    def _set_ids(self, cr, uid, name, tt, lang, ids, value, src=None):
        self.clear_caches()
        cr.execute(
            'update ir_translation '
            'set value=%s '
            '  , src=%s '
            '  , state=%s '
            'where lang=%s '
            'and type=%s '
            'and name=%s '
            'and res_id IN %s '
            'returning res_id', (
                value,
                src,
                'translated',
                lang,
                tt,
                name,
                tuple(ids),
            ))

        existing_ids = [x[0] for x in cr.fetchall()]

        for id in list(set(ids) - set(existing_ids)):
            self.create(
                cr, uid, {
                    'lang': lang,
                    'type': tt,
                    'name': name,
                    'res_id': id,
                    'value': value,
                    'src': src,
                    'state': 'translated'
                })
        return len(ids)

    def _get_source_query(self, cr, uid, name, types, lang, source, res_id):
        if source:
            # Note: the extra test on md5(src) is a hint for postgres to use the
            # index ir_translation_src_md5
            query = """SELECT value
                       FROM ir_translation
                       WHERE lang=%s
                        AND type in %s
                        AND src=%s AND md5(src)=md5(%s)"""
            source = tools.ustr(source)
            params = (lang or '', types, source, source)
            if res_id:
                query += " AND res_id in %s"
                params += (res_id, )
            if name:
                query += " AND name=%s"
                params += (tools.ustr(name), )
        else:
            query = """SELECT value
                       FROM ir_translation
                       WHERE lang=%s
                        AND type in %s
                        AND name=%s"""

            params = (lang or '', types, tools.ustr(name))

        return (query, params)

    @tools.ormcache('name', 'types', 'lang', 'source', 'res_id')
    def __get_source(self, cr, uid, name, types, lang, source, res_id):
        # res_id is a tuple or None, otherwise ormcache cannot cache it!
        query, params = self._get_source_query(cr, uid, name, types, lang,
                                               source, res_id)
        cr.execute(query, params)
        res = cr.fetchone()
        trad = res and res[0] or u''
        if source and not trad:
            return tools.ustr(source)
        return trad

    def _get_source(self,
                    cr,
                    uid,
                    name,
                    types,
                    lang,
                    source=None,
                    res_id=None):
        """
        Returns the translation for the given combination of name, type, language
        and source. All values passed to this method should be unicode (not byte strings),
        especially ``source``.

        :param name: identification of the term to translate, such as field name (optional if source is passed)
        :param types: single string defining type of term to translate (see ``type`` field on ir.translation), or sequence of allowed types (strings)
        :param lang: language code of the desired translation
        :param source: optional source term to translate (should be unicode)
        :param res_id: optional resource id or a list of ids to translate (if used, ``source`` should be set)
        :rtype: unicode
        :return: the request translation, or an empty unicode string if no translation was
                 found and `source` was not passed
        """
        # FIXME: should assert that `source` is unicode and fix all callers to always pass unicode
        # so we can remove the string encoding/decoding.
        if not lang:
            return tools.ustr(source or '')
        if isinstance(types, basestring):
            types = (types, )
        if res_id:
            if isinstance(res_id, (int, long)):
                res_id = (res_id, )
            else:
                res_id = tuple(res_id)
        return self.__get_source(cr, uid, name, types, lang, source, res_id)

    @api.model
    def _get_terms_query(self, field, records):
        """ Utility function that makes the query for field terms. """
        query = """ SELECT * FROM ir_translation
                    WHERE lang=%s AND type=%s AND name=%s AND res_id IN %s """
        name = "%s,%s" % (field.model_name, field.name)
        params = (records.env.lang, 'model', name, tuple(records.ids))
        return query, params

    @api.model
    def _get_terms_mapping(self, field, records):
        """ Return a function mapping a ir_translation row (dict) to a value.
        This method is called before querying the database for translations.
        """
        return lambda data: data['value']

    @api.model
    def _get_terms_translations(self, field, records):
        """ Return the terms and translations of a given `field` on `records`.

        :return: {record_id: {source: value}}
        """
        result = {rid: {} for rid in records.ids}
        if records:
            map_trans = self._get_terms_mapping(field, records)
            query, params = self._get_terms_query(field, records)
            self._cr.execute(query, params)
            for data in self._cr.dictfetchall():
                result[data['res_id']][data['src']] = map_trans(data)
        return result

    @api.model
    def _sync_terms_translations(self, field, records):
        """ Synchronize the translations to the terms to translate, after the
        English value of a field is modified. The algorithm tries to match
        existing translations to the terms to translate, provided the distance
        between modified strings is not too large. It allows to not retranslate
        data where a typo has been fixed in the English value.
        """
        if not callable(getattr(field, 'translate', None)):
            return

        trans = self.env['ir.translation']
        outdated = trans
        discarded = trans

        for record in records:
            # get field value and terms to translate
            value = record[field.name]
            terms = set(field.get_trans_terms(value))
            record_trans = trans.search([
                ('type', '=', 'model'),
                ('name', '=', "%s,%s" % (field.model_name, field.name)),
                ('res_id', '=', record.id),
            ])

            if not terms:
                # discard all translations for that field
                discarded += record_trans
                continue

            # remap existing translations on terms when possible
            for trans in record_trans:
                if trans.src == trans.value:
                    discarded += trans
                elif trans.src not in terms:
                    matches = get_close_matches(trans.src, terms, 1, 0.9)
                    if matches:
                        trans.write({'src': matches[0], 'state': trans.state})
                    else:
                        outdated += trans

        # process outdated and discarded translations
        outdated.write({'state': 'to_translate'})
        discarded.unlink()

    @api.model
    @tools.ormcache_context('model_name', keys=('lang', ))
    def get_field_string(self, model_name):
        """ Return the translation of fields strings in the context's language.
        Note that the result contains the available translations only.

        :param model_name: the name of a model
        :return: the model's fields' strings as a dictionary `{field_name: field_string}`
        """
        fields = self.env['ir.model.fields'].search([('model', '=', model_name)
                                                     ])
        return {field.name: field.field_description for field in fields}

    @api.model
    @tools.ormcache_context('model_name', keys=('lang', ))
    def get_field_help(self, model_name):
        """ Return the translation of fields help in the context's language.
        Note that the result contains the available translations only.

        :param model_name: the name of a model
        :return: the model's fields' help as a dictionary `{field_name: field_help}`
        """
        fields = self.env['ir.model.fields'].search([('model', '=', model_name)
                                                     ])
        return {field.name: field.help for field in fields}

    @api.multi
    def check(self, mode):
        """ Check access rights of operation ``mode`` on ``self`` for the
        current user. Raise an AccessError in case conditions are not met.
        """
        if self.env.user._is_admin():
            return

        # collect translated field records (model_ids) and other translations
        trans_ids = []
        model_ids = defaultdict(list)
        model_fields = defaultdict(list)
        for trans in self:
            if trans.type == 'model':
                mname, fname = trans.name.split(',')
                model_ids[mname].append(trans.res_id)
                model_fields[mname].append(fname)
            else:
                trans_ids.append(trans.id)

        # check for regular access rights on other translations
        if trans_ids:
            records = self.browse(trans_ids)
            records.check_access_rights(mode)
            records.check_access_rule(mode)

        # check for read/write access on translated field records
        fmode = 'read' if mode == 'read' else 'write'
        for mname, ids in model_ids.iteritems():
            records = self.env[mname].browse(ids)
            records.check_access_rights(fmode)
            records.check_field_access_rights(fmode, model_fields[mname])
            records.check_access_rule(fmode)

    @api.model
    def create(self, vals):
        if vals.get('type') == 'model' and vals.get('value'):
            # check and sanitize value
            mname, fname = vals['name'].split(',')
            field = self.env[mname]._fields[fname]
            vals['value'] = field.check_trans_value(vals['value'])
        record = super(ir_translation,
                       self.sudo()).create(vals).with_env(self.env)
        record.check('create')
        self.clear_caches()
        return record

    @api.multi
    def write(self, vals):
        if vals.get('value'):
            vals.setdefault('state', 'translated')
            ttype = vals.get('type') or self[:1].type
            if ttype == 'model':
                # check and sanitize value
                name = vals.get('name') or self[:1].name
                mname, fname = name.split(',')
                field = self.env[mname]._fields[fname]
                vals['value'] = field.check_trans_value(vals['value'])
        elif vals.get('src') or not vals.get('value', True):
            vals.setdefault('state', 'to_translate')
        self.check('write')
        result = super(ir_translation, self.sudo()).write(vals)
        self.check('write')
        self.clear_caches()
        return result

    @api.multi
    def unlink(self):
        self.check('unlink')
        self.clear_caches()
        return super(ir_translation, self.sudo()).unlink()

    @api.model
    def insert_missing(self, field, records):
        """ Insert missing translations for `field` on `records`. """
        records = records.with_context(lang=None)
        external_ids = records.get_external_id()  # if no xml_id, empty string
        if callable(field.translate):
            # insert missing translations for each term in src
            query = """ INSERT INTO ir_translation (lang, type, name, res_id, src, value, module)
                        SELECT l.code, 'model', %(name)s, %(res_id)s, %(src)s, %(src)s, %(module)s
                        FROM res_lang l
                        WHERE NOT EXISTS (
                            SELECT 1 FROM ir_translation
                            WHERE lang=l.code AND type='model' AND name=%(name)s AND res_id=%(res_id)s AND src=%(src)s AND module=%(module)s
                        );
                    """
            for record in records:
                module = external_ids[record.id].split('.')[0]
                src = record[field.name] or None
                for term in set(field.get_trans_terms(src)):
                    self._cr.execute(
                        query, {
                            'name': "%s,%s" % (field.model_name, field.name),
                            'res_id': record.id,
                            'src': term,
                            'module': module
                        })
        else:
            # insert missing translations for src
            query = """ INSERT INTO ir_translation (lang, type, name, res_id, src, value, module)
                        SELECT l.code, 'model', %(name)s, %(res_id)s, %(src)s, %(src)s, %(module)s
                        FROM res_lang l
                        WHERE l.code != 'en_US' AND NOT EXISTS (
                            SELECT 1 FROM ir_translation
                            WHERE lang=l.code AND type='model' AND name=%(name)s AND res_id=%(res_id)s AND module=%(module)s
                        );
                        UPDATE ir_translation SET src=%(src)s
                        WHERE type='model' AND name=%(name)s AND res_id=%(res_id)s AND module=%(module)s;
                    """
            for record in records:
                module = external_ids[record.id].split('.')[0]
                self._cr.execute(
                    query, {
                        'name': "%s,%s" % (field.model_name, field.name),
                        'res_id': record.id,
                        'src': record[field.name] or None,
                        'module': module
                    })
        self.clear_caches()

    @api.model
    def translate_fields(self, model, id, field=None):
        """ Open a view for translating the field(s) of the record (model, id). """
        main_lang = 'en_US'
        if not self.env['res.lang'].search_count([('code', '!=', main_lang)]):
            raise UserError(
                _("Translation features are unavailable until you install an extra translation."
                  ))

        # determine domain for selecting translations
        record = self.env[model].with_context(lang=main_lang).browse(id)
        domain = ['&', ('res_id', '=', id), ('name', '=like', model + ',%')]

        def make_domain(fld, rec):
            name = "%s,%s" % (fld.model_name, fld.name)
            return ['&', ('res_id', '=', rec.id), ('name', '=', name)]

        # insert missing translations, and extend domain for related fields
        for name, fld in record._fields.items():
            if not getattr(fld, 'translate', False):
                continue

            rec = record
            if fld.related:
                try:
                    # traverse related fields up to their data source
                    while fld.related:
                        rec, fld = fld.traverse_related(rec)
                    if rec:
                        domain = ['|'] + domain + make_domain(fld, rec)
                except AccessError:
                    continue

            assert fld.translate and rec._name == fld.model_name
            self.insert_missing(fld, rec)

        action = {
            'name': 'Translate',
            'res_model': 'ir.translation',
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'domain': domain,
        }
        if field:
            fld = record._fields[field]
            if not fld.related:
                action['context'] = {
                    'search_default_name':
                    "%s,%s" % (fld.model_name, fld.name),
                }
        return action

    def _get_import_cursor(self, cr, uid, context=None):
        """ Return a cursor-like object for fast inserting translations
        """
        return ir_translation_import_cursor(cr, uid, self, context=context)

    def load_module_terms(self, cr, modules, langs, context=None):
        context = dict(context or {})  # local copy
        for module_name in modules:
            modpath = ecore.modules.get_module_path(module_name)
            if not modpath:
                continue
            for lang in langs:
                lang_code = tools.get_iso_codes(lang)
                base_lang_code = None
                if '_' in lang_code:
                    base_lang_code = lang_code.split('_')[0]

                # Step 1: for sub-languages, load base language first (e.g. es_CL.po is loaded over es.po)
                if base_lang_code:
                    base_trans_file = ecore.modules.get_module_resource(
                        module_name, 'i18n', base_lang_code + '.po')
                    if base_trans_file:
                        _logger.info(
                            'module %s: loading base translation file %s for language %s',
                            module_name, base_lang_code, lang)
                        tools.trans_load(cr,
                                         base_trans_file,
                                         lang,
                                         verbose=False,
                                         module_name=module_name,
                                         context=context)
                        context[
                            'overwrite'] = True  # make sure the requested translation will override the base terms later

                    # i18n_extra folder is for additional translations handle manually (eg: for l10n_be)
                    base_trans_extra_file = ecore.modules.get_module_resource(
                        module_name, 'i18n_extra', base_lang_code + '.po')
                    if base_trans_extra_file:
                        _logger.info(
                            'module %s: loading extra base translation file %s for language %s',
                            module_name, base_lang_code, lang)
                        tools.trans_load(cr,
                                         base_trans_extra_file,
                                         lang,
                                         verbose=False,
                                         module_name=module_name,
                                         context=context)
                        context[
                            'overwrite'] = True  # make sure the requested translation will override the base terms later

                # Step 2: then load the main translation file, possibly overriding the terms coming from the base language
                trans_file = ecore.modules.get_module_resource(
                    module_name, 'i18n', lang_code + '.po')
                if trans_file:
                    _logger.info(
                        'module %s: loading translation file (%s) for language %s',
                        module_name, lang_code, lang)
                    tools.trans_load(cr,
                                     trans_file,
                                     lang,
                                     verbose=False,
                                     module_name=module_name,
                                     context=context)
                elif lang_code != 'en_US':
                    _logger.info('module %s: no translation for language %s',
                                 module_name, lang_code)

                trans_extra_file = ecore.modules.get_module_resource(
                    module_name, 'i18n_extra', lang_code + '.po')
                if trans_extra_file:
                    _logger.info(
                        'module %s: loading extra translation file (%s) for language %s',
                        module_name, lang_code, lang)
                    tools.trans_load(cr,
                                     trans_extra_file,
                                     lang,
                                     verbose=False,
                                     module_name=module_name,
                                     context=context)
        return True
예제 #21
0
class PaymentTransaction(osv.Model):
    """ Transaction Model. Each specific acquirer can extend the model by adding
    its own fields.

    Methods that can be added in an acquirer-specific implementation:

     - ``<name>_create``: method receiving values used when creating a new
       transaction and that returns a dictionary that will update those values.
       This method can be used to tweak some transaction values.

    Methods defined for convention, depending on your controllers:

     - ``<name>_form_feedback(self, cr, uid, data, context=None)``: method that
       handles the data coming from the acquirer after the transaction. It will
       generally receives data posted by the acquirer after the transaction.
    """
    _name = 'payment.transaction'
    _description = 'Payment Transaction'
    _order = 'id desc'
    _rec_name = 'reference'

    def _lang_get(self, cr, uid, context=None):
        lang_ids = self.pool['res.lang'].search(cr, uid, [], context=context)
        languages = self.pool['res.lang'].browse(cr, uid, lang_ids, context=context)
        return [(language.code, language.name) for language in languages]

    def _default_partner_country_id(self, cr, uid, context=None):
        comp = self.pool['res.company'].browse(cr, uid, context.get('company_id', 1), context=context)
        return comp.country_id.id

    _columns = {
        'create_date': fields.datetime('Creation Date', readonly=True),
        'date_validate': fields.datetime('Validation Date'),
        'acquirer_id': fields.many2one(
            'payment.acquirer', 'Acquirer',
            required=True,
        ),
        'type': fields.selection(
            [('server2server', 'Server To Server'), ('form', 'Form'), ('form_save', 'Form with credentials storage')],
            string='Type', required=True),
        'state': fields.selection(
            [('draft', 'Draft'), ('pending', 'Pending'),
             ('done', 'Done'), ('error', 'Error'),
             ('cancel', 'Canceled')
             ], 'Status', required=True,
            track_visibility='onchange', copy=False),
        'state_message': fields.text('Message',
                                     help='Field used to store error and/or validation messages for information'),
        # payment
        'amount': fields.float('Amount', required=True,
                               digits=(16, 2),
                               track_visibility='always',
                               help='Amount'),
        'fees': fields.float('Fees',
                             digits=(16, 2),
                             track_visibility='always',
                             help='Fees amount; set by the system because depends on the acquirer'),
        'currency_id': fields.many2one('res.currency', 'Currency', required=True),
        'reference': fields.char('Reference', required=True, help='Internal reference of the TX'),
        'acquirer_reference': fields.char('Acquirer Reference',
                                          help='Reference of the TX as stored in the acquirer database'),
        # duplicate partner / transaction data to store the values at transaction time
        'partner_id': fields.many2one('res.partner', 'Partner', track_visibility='onchange',),
        'partner_name': fields.char('Partner Name'),
        'partner_lang': fields.selection(_lang_get, 'Language'),
        'partner_email': fields.char('Email'),
        'partner_zip': fields.char('Zip'),
        'partner_address': fields.char('Address'),
        'partner_city': fields.char('City'),
        'partner_country_id': fields.many2one('res.country', 'Country', required=True),
        'partner_phone': fields.char('Phone'),
        'html_3ds': fields.char('3D Secure HTML'),

        'callback_eval': fields.char('S2S Callback', help="""\
            Will be safe_eval with `self` being the current transaction. i.e.:
                self.env['my.model'].payment_validated(self)""", oldname="s2s_cb_eval"),
        'payment_method_id': fields.many2one('payment.method', 'Payment Method', domain="[('acquirer_id', '=', acquirer_id)]"),
    }

    def _check_reference(self, cr, uid, ids, context=None):
        transaction = self.browse(cr, uid, ids[0], context=context)
        if transaction.state not in ['cancel', 'error']:
            if self.search(cr, uid, [('reference', '=', transaction.reference), ('id', '!=', transaction.id)], context=context, count=True):
                return False
        return True

    _constraints = [
        (_check_reference, 'The payment transaction reference must be unique!', ['reference', 'state']),
    ]

    _defaults = {
        'type': 'form',
        'state': 'draft',
        'partner_lang': 'en_US',
        'partner_country_id': _default_partner_country_id,
        'reference': lambda s, c, u, ctx=None: s.pool['ir.sequence'].next_by_code(c, u, 'payment.transaction', context=ctx),
    }

    def create(self, cr, uid, values, context=None):
        Acquirer = self.pool['payment.acquirer']

        if values.get('partner_id'):  # @TDENOTE: not sure
            values.update(self.on_change_partner_id(cr, uid, None, values.get('partner_id'), context=context)['value'])

        # 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)

            # compute fees
            custom_method_name = '%s_compute_fees' % acquirer.provider
            if hasattr(Acquirer, custom_method_name):
                fees = getattr(Acquirer, custom_method_name)(
                    cr, uid, acquirer.id, values.get('amount', 0.0), values.get('currency_id'), values.get('partner_country_id'), context=None)
                values['fees'] = float_round(fees, 2)

            # 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))

        # Default value of reference is
        tx_id = super(PaymentTransaction, self).create(cr, uid, values, context=context)
        if not values.get('reference'):
            self.write(cr, uid, [tx_id], {'reference': str(tx_id)}, context=context)
        return tx_id

    def write(self, cr, uid, ids, values, context=None):
        Acquirer = self.pool['payment.acquirer']
        if ('acquirer_id' in values or 'amount' in values) and 'fees' not in values:
            # The acquirer or the amount has changed, and the fees are not explicitely forced. Fees must be recomputed.
            if isinstance(ids, (int, long)):
                ids = [ids]
            for txn_id in ids:
                vals = dict(values)
                vals['fees'] = 0.0
                transaction = self.browse(cr, uid, txn_id, context=context)
                if 'acquirer_id' in values:
                    acquirer = Acquirer.browse(cr, uid, values['acquirer_id'], context=context) if values['acquirer_id'] else None
                else:
                    acquirer = transaction.acquirer_id
                if acquirer:
                    custom_method_name = '%s_compute_fees' % acquirer.provider
                    if hasattr(Acquirer, custom_method_name):
                        amount = (values['amount'] if 'amount' in values else transaction.amount) or 0.0
                        currency_id = values.get('currency_id') or transaction.currency_id.id
                        country_id = values.get('partner_country_id') or transaction.partner_country_id.id
                        fees = getattr(Acquirer, custom_method_name)(cr, uid, acquirer.id, amount, currency_id, country_id, context=None)
                        vals['fees'] = float_round(fees, 2)
                res = super(PaymentTransaction, self).write(cr, uid, txn_id, vals, context=context)
            return res
        return super(PaymentTransaction, self).write(cr, uid, ids, values, context=context)

    def on_change_partner_id(self, cr, uid, ids, partner_id, context=None):
        partner = None
        if partner_id:
            partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context)
            return {'value': {
                'partner_name': partner and partner.name or False,
                'partner_lang': partner and partner.lang or 'en_US',
                'partner_email': partner and partner.email or False,
                'partner_zip': partner and partner.zip or False,
                'partner_address': _partner_format_address(partner and partner.street or '', partner and partner.street2 or ''),
                'partner_city': partner and partner.city or False,
                'partner_country_id': partner and partner.country_id.id or self._default_partner_country_id(cr, uid, context=context),
                'partner_phone': partner and partner.phone or False,
            }}
        return {}

    def get_next_reference(self, cr, uid, reference, context=None):
        ref_suffix = 1
        init_ref = reference
        while self.pool['payment.transaction'].search_count(cr, uid, [('reference', '=', reference)], context=context):
            reference = init_ref + '-' + str(ref_suffix)
            ref_suffix += 1
        return reference

    # --------------------------------------------------
    # FORM RELATED METHODS
    # --------------------------------------------------

    def render(self, cr, uid, id, context=None):
        tx = self.browse(cr, uid, id, context=context)
        values = {
            'reference': tx.reference,
            'amount': tx.amount,
            'currency_id': tx.currency_id.id,
            'currency': tx.currency_id,
            'partner': tx.partner_id,
            'partner_name': tx.partner_name,
            'partner_lang': tx.partner_lang,
            'partner_email': tx.partner_email,
            'partner_zip': tx.partner_zip,
            'partner_address': tx.partner_address,
            'partner_city': tx.partner_city,
            'partner_country_id': tx.partner_country_id.id,
            'partner_country': tx.partner_country_id,
            'partner_phone': tx.partner_phone,
            'partner_state': None,
        }
        return tx.acquirer_id.render(None, None, None, values=values)

    def form_feedback(self, cr, uid, data, acquirer_name, context=None):
        invalid_parameters, tx = None, None

        tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name
        if hasattr(self, tx_find_method_name):
            tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context)

        invalid_param_method_name = '_%s_form_get_invalid_parameters' % acquirer_name
        if hasattr(self, invalid_param_method_name):
            invalid_parameters = getattr(self, invalid_param_method_name)(cr, uid, tx, data, context=context)

        if invalid_parameters:
            _error_message = '%s: incorrect tx data:\n' % (acquirer_name)
            for item in invalid_parameters:
                _error_message += '\t%s: received %s instead of %s\n' % (item[0], item[1], item[2])
            _logger.error(_error_message)
            return False

        feedback_method_name = '_%s_form_validate' % acquirer_name
        if hasattr(self, feedback_method_name):
            return getattr(self, feedback_method_name)(cr, uid, tx, data, context=context)

        return True

    # --------------------------------------------------
    # SERVER2SERVER RELATED METHODS
    # --------------------------------------------------
    def s2s_create(self, cr, uid, values, cc_values, context=None):
        tx_id, tx_result = self.s2s_send(cr, uid, values, cc_values, context=context)
        self.s2s_feedback(cr, uid, tx_id, tx_result, context=context)
        return tx_id

    def s2s_do_transaction(self, cr, uid, id, context=None, **kwargs):
        tx = self.browse(cr, uid, id, context=context)
        custom_method_name = '%s_s2s_do_transaction' % tx.acquirer_id.provider
        if hasattr(self, custom_method_name):
            return getattr(self, custom_method_name)(cr, uid, id, context=context, **kwargs)

    def s2s_get_tx_status(self, cr, uid, tx_id, context=None):
        """ Get the tx status. """
        tx = self.browse(cr, uid, tx_id, context=context)

        invalid_param_method_name = '_%s_s2s_get_tx_status' % tx.acquirer_id.provider
        if hasattr(self, invalid_param_method_name):
            return getattr(self, invalid_param_method_name)(cr, uid, tx, context=context)

        return True
예제 #22
0
파일: project_issue.py 프로젝트: ecoreos/hz
class project_issue(osv.Model):
    _name = "project.issue"
    _description = "Project Issue"
    _order = "priority desc, create_date desc"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _mail_post_access = 'read'

    def _get_default_partner(self, cr, uid, context=None):
        if context is None:
            context = {}
        if 'default_project_id' in context:
            project = self.pool.get('project.project').browse(
                cr, uid, context['default_project_id'], context=context)
            if project and project.partner_id:
                return project.partner_id.id
        return False

    def _get_default_stage_id(self, cr, uid, context=None):
        """ Gives default stage_id """
        if context is None:
            context = {}
        return self.stage_find(cr,
                               uid, [],
                               context.get('default_project_id'),
                               [('fold', '=', False)],
                               context=context)

    def _read_group_stage_ids(self,
                              cr,
                              uid,
                              ids,
                              domain,
                              read_group_order=None,
                              access_rights_uid=None,
                              context=None):
        if context is None:
            context = {}
        access_rights_uid = access_rights_uid or uid
        stage_obj = self.pool.get('project.task.type')
        order = stage_obj._order
        # lame hack to allow reverting search, should just work in the trivial case
        if read_group_order == 'stage_id desc':
            order = "%s desc" % order
        # retrieve team_id from the context, add them to already fetched columns (ids)
        if 'default_project_id' in context:
            search_domain = [
                '|', ('project_ids', '=', context['default_project_id']),
                ('id', 'in', ids)
            ]
        else:
            search_domain = [('id', 'in', ids)]
        # perform search
        stage_ids = stage_obj._search(cr,
                                      uid,
                                      search_domain,
                                      order=order,
                                      access_rights_uid=access_rights_uid,
                                      context=context)
        result = stage_obj.name_get(cr,
                                    access_rights_uid,
                                    stage_ids,
                                    context=context)
        # restore order of the search
        result.sort(
            lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))

        fold = {}
        for stage in stage_obj.browse(cr,
                                      access_rights_uid,
                                      stage_ids,
                                      context=context):
            fold[stage.id] = stage.fold or False
        return result, fold

    def _compute_day(self, cr, uid, ids, fields, args, context=None):
        """
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of Openday’s IDs
        @return: difference between current date and log date
        @param context: A standard dictionary for contextual values
        """
        Calendar = self.pool['resource.calendar']

        res = dict((res_id, {}) for res_id in ids)
        for issue in self.browse(cr, uid, ids, context=context):
            values = {
                'day_open': 0.0,
                'day_close': 0.0,
                'working_hours_open': 0.0,
                'working_hours_close': 0.0,
                'days_since_creation': 0.0,
                'inactivity_days': 0.0,
            }
            # if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5), represented by None
            calendar_id = None
            if issue.project_id and issue.project_id.resource_calendar_id:
                calendar_id = issue.project_id.resource_calendar_id.id

            dt_create_date = datetime.strptime(issue.create_date,
                                               DEFAULT_SERVER_DATETIME_FORMAT)

            if issue.date_open:
                dt_date_open = datetime.strptime(
                    issue.date_open, DEFAULT_SERVER_DATETIME_FORMAT)
                values['day_open'] = (dt_date_open - dt_create_date
                                      ).total_seconds() / (24.0 * 3600)
                values['working_hours_open'] = Calendar._interval_hours_get(
                    cr,
                    uid,
                    calendar_id,
                    dt_create_date,
                    dt_date_open,
                    timezone_from_uid=issue.user_id.id or uid,
                    exclude_leaves=False,
                    context=context)

            if issue.date_closed:
                dt_date_closed = datetime.strptime(
                    issue.date_closed, DEFAULT_SERVER_DATETIME_FORMAT)
                values['day_close'] = (dt_date_closed - dt_create_date
                                       ).total_seconds() / (24.0 * 3600)
                values['working_hours_close'] = Calendar._interval_hours_get(
                    cr,
                    uid,
                    calendar_id,
                    dt_create_date,
                    dt_date_closed,
                    timezone_from_uid=issue.user_id.id or uid,
                    exclude_leaves=False,
                    context=context)

            days_since_creation = datetime.today() - dt_create_date
            values['days_since_creation'] = days_since_creation.days
            if issue.date_action_last:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.date_action_last, DEFAULT_SERVER_DATETIME_FORMAT)
            elif issue.date_last_stage_update:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.date_last_stage_update,
                    DEFAULT_SERVER_DATETIME_FORMAT)
            else:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT)
            values['inactivity_days'] = inactive_days.days

            # filter only required values
            for field in fields:
                res[issue.id][field] = values[field]

        return res

    def on_change_project(self, cr, uid, ids, project_id, context=None):
        if project_id:
            project = self.pool.get('project.project').browse(cr,
                                                              uid,
                                                              project_id,
                                                              context=context)
            if project and project.partner_id:
                return {'value': {'partner_id': project.partner_id.id}}
        return {'value': {'partner_id': False}}

    _columns = {
        'id': fields.integer('ID', readonly=True),
        'name': fields.char('Issue', required=True),
        'active': fields.boolean('Active', required=False),
        'create_date': fields.datetime('Creation Date', readonly=True, select=True),
        'write_date': fields.datetime('Update Date', readonly=True),
        'days_since_creation': fields.function(_compute_day, string='Days since creation date', \
                                               multi='compute_day', type="integer", help="Difference in days between creation date and current date"),
        'date_deadline': fields.date('Deadline'),
        'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id',\
                        select=True, help='Sales team to which Case belongs to.\
                             Define Responsible user and Email account for mail gateway.'                                                                                         ),
        'partner_id': fields.many2one('res.partner', 'Contact', select=1),
        'company_id': fields.many2one('res.company', 'Company'),
        'description': fields.text('Private Note'),
        'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
                                         track_visibility='onchange',
                                         help="A Issue's kanban state indicates special situations affecting it:\n"
                                              " * Normal is the default situation\n"
                                              " * Blocked indicates something is preventing the progress of this issue\n"
                                              " * Ready for next stage indicates the issue is ready to be pulled to the next stage",
                                         required=True),
        'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1),
        'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
        'date_open': fields.datetime('Assigned', readonly=True, select=True),
        # Project Issue fields
        'date_closed': fields.datetime('Closed', readonly=True, select=True),
        'date': fields.datetime('Date'),
        'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
        'channel': fields.char('Channel', help="Communication channel."),
        'tag_ids': fields.many2many('project.tags', string='Tags'),
        'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority', select=True),
        'stage_id': fields.many2one ('project.task.type', 'Stage',
                        track_visibility='onchange', select=True,
                        domain="[('project_ids', '=', project_id)]", copy=False),
        'project_id': fields.many2one('project.project', 'Project', track_visibility='onchange', select=True),
        'duration': fields.float('Duration'),
        'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]",
            help="You can link this issue to an existing task or directly create a new one from here"),
        'day_open': fields.function(_compute_day, string='Days to Assign',
                                    multi='compute_day', type="float",
                                    store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
        'day_close': fields.function(_compute_day, string='Days to Close',
                                     multi='compute_day', type="float",
                                     store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
        'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1, track_visibility='onchange'),
        'working_hours_open': fields.function(_compute_day, string='Working Hours to assign the Issue',
                                              multi='compute_day', type="float",
                                              store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
        'working_hours_close': fields.function(_compute_day, string='Working Hours to close the Issue',
                                               multi='compute_day', type="float",
                                               store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
        'inactivity_days': fields.function(_compute_day, string='Days since last action',
                                           multi='compute_day', type="integer", help="Difference in days between last action and current date"),
        'color': fields.integer('Color Index'),
        'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
        'date_action_last': fields.datetime('Last Action', readonly=1),
        'date_action_next': fields.datetime('Next Action', readonly=1),
        'legend_blocked': fields.related("stage_id", "legend_blocked", type="char", string='Kanban Blocked Explanation'),
        'legend_done': fields.related("stage_id", "legend_done", type="char", string='Kanban Valid Explanation'),
        'legend_normal': fields.related("stage_id", "legend_normal", type="char", string='Kanban Ongoing Explanation'),
    }

    _defaults = {
        'active':
        1,
        'team_id':
        lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(
            cr, uid, context=c),
        'stage_id':
        lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
        'company_id':
        lambda s, cr, uid, c: s.pool['res.users']._get_company(
            cr, uid, context=c),
        'priority':
        '0',
        'kanban_state':
        'normal',
        'date_last_stage_update':
        fields.datetime.now,
        'user_id':
        lambda obj, cr, uid, context: uid,
    }

    _group_by_full = {'stage_id': _read_group_stage_ids}

    def copy(self, cr, uid, id, default=None, context=None):
        issue = self.read(cr, uid, [id], ['name'], context=context)[0]
        if not default:
            default = {}
        default = default.copy()
        default.update(name=_('%s (copy)') % (issue['name']))
        return super(project_issue, self).copy(cr,
                                               uid,
                                               id,
                                               default=default,
                                               context=context)

    def create(self, cr, uid, vals, context=None):
        context = dict(context or {})
        if vals.get('project_id') and not context.get('default_project_id'):
            context['default_project_id'] = vals.get('project_id')
        if vals.get('user_id') and not vals.get('date_open'):
            vals['date_open'] = fields.datetime.now()
        if 'stage_id' in vals:
            vals.update(
                self.onchange_stage_id(cr,
                                       uid,
                                       None,
                                       vals.get('stage_id'),
                                       context=context)['value'])

        # context: no_log, because subtype already handle this
        create_context = dict(context, mail_create_nolog=True)
        return super(project_issue, self).create(cr,
                                                 uid,
                                                 vals,
                                                 context=create_context)

    def write(self, cr, uid, ids, vals, context=None):
        # stage change: update date_last_stage_update
        if 'stage_id' in vals:
            vals.update(
                self.onchange_stage_id(cr,
                                       uid,
                                       ids,
                                       vals.get('stage_id'),
                                       context=context)['value'])
            vals['date_last_stage_update'] = fields.datetime.now()
            if 'kanban_state' not in vals:
                vals['kanban_state'] = 'normal'
        # user_id change: update date_open
        if vals.get('user_id') and 'date_open' not in vals:
            vals['date_open'] = fields.datetime.now()

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

    def onchange_task_id(self, cr, uid, ids, task_id, context=None):
        if not task_id:
            return {'value': {}}
        task = self.pool.get('project.task').browse(cr,
                                                    uid,
                                                    task_id,
                                                    context=context)
        return {
            'value': {
                'user_id': task.user_id.id,
            }
        }

    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
        """ This function returns value of partner email address based on partner
            :param part: Partner's id
        """
        if partner_id:
            partner = self.pool['res.partner'].browse(cr, uid, partner_id,
                                                      context)
            return {'value': {'email_from': partner.email}}
        return {'value': {'email_from': False}}

    def get_empty_list_help(self, cr, uid, help, context=None):
        context = dict(context or {})
        context['empty_list_help_model'] = 'project.project'
        context['empty_list_help_id'] = context.get('default_project_id')
        context['empty_list_help_document_name'] = _("issues")
        return super(project_issue, self).get_empty_list_help(cr,
                                                              uid,
                                                              help,
                                                              context=context)

    # -------------------------------------------------------
    # Stage management
    # -------------------------------------------------------

    def onchange_stage_id(self, cr, uid, ids, stage_id, context=None):
        if not stage_id:
            return {'value': {}}
        stage = self.pool['project.task.type'].browse(cr,
                                                      uid,
                                                      stage_id,
                                                      context=context)
        if stage.fold:
            return {'value': {'date_closed': fields.datetime.now()}}
        return {'value': {'date_closed': False}}

    def stage_find(self,
                   cr,
                   uid,
                   cases,
                   team_id,
                   domain=[],
                   order='sequence',
                   context=None):
        """ Override of the base.stage method
            Parameter of the stage search taken from the issue:
            - type: stage type must be the same or 'both'
            - team_id: if set, stages must belong to this team or
              be a default case
        """
        if isinstance(cases, (int, long)):
            cases = self.browse(cr, uid, cases, context=context)
        # collect all team_ids
        team_ids = []
        if team_id:
            team_ids.append(team_id)
        for task in cases:
            if task.project_id:
                team_ids.append(task.project_id.id)
        # OR all team_ids and OR with case_default
        search_domain = []
        if team_ids:
            search_domain += [('|')] * (len(team_ids) - 1)
            for team_id in team_ids:
                search_domain.append(('project_ids', '=', team_id))
        search_domain += list(domain)
        # perform search, return the first found
        stage_ids = self.pool.get('project.task.type').search(cr,
                                                              uid,
                                                              search_domain,
                                                              order=order,
                                                              context=context)
        if stage_ids:
            return stage_ids[0]
        return False

    # -------------------------------------------------------
    # Mail gateway
    # -------------------------------------------------------

    def _track_subtype(self, cr, uid, ids, init_values, context=None):
        record = self.browse(cr, uid, ids[0], context=context)
        if 'kanban_state' in init_values and record.kanban_state == 'blocked':
            return 'project_issue.mt_issue_blocked'
        elif 'kanban_state' in init_values and record.kanban_state == 'done':
            return 'project_issue.mt_issue_ready'
        elif 'user_id' in init_values and record.user_id:  # assigned -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1:  # start stage -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values:
            return 'project_issue.mt_issue_stage'
        return super(project_issue, self)._track_subtype(cr,
                                                         uid,
                                                         ids,
                                                         init_values,
                                                         context=context)

    def _notification_group_recipients(self,
                                       cr,
                                       uid,
                                       ids,
                                       message,
                                       recipients,
                                       done_ids,
                                       group_data,
                                       context=None):
        """ Override the mail.thread method to handle project users and officers
        recipients. Indeed those will have specific action in their notification
        emails: creating tasks, assigning it. """
        group_project_user = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'project.group_project_user')
        for recipient in recipients:
            if recipient.id in done_ids:
                continue
            if recipient.user_ids and group_project_user in recipient.user_ids[
                    0].groups_id.ids:
                group_data['group_project_user'] |= recipient
                done_ids.add(recipient.id)
        return super(project_issue,
                     self)._notification_group_recipients(cr,
                                                          uid,
                                                          ids,
                                                          message,
                                                          recipients,
                                                          done_ids,
                                                          group_data,
                                                          context=context)

    def _notification_get_recipient_groups(self,
                                           cr,
                                           uid,
                                           ids,
                                           message,
                                           recipients,
                                           context=None):
        res = super(project_issue,
                    self)._notification_get_recipient_groups(cr,
                                                             uid,
                                                             ids,
                                                             message,
                                                             recipients,
                                                             context=context)

        new_action_id = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'project_issue.project_issue_categ_act0')
        take_action = self._notification_link_helper(cr,
                                                     uid,
                                                     ids,
                                                     'assign',
                                                     context=context)
        new_action = self._notification_link_helper(cr,
                                                    uid,
                                                    ids,
                                                    'new',
                                                    context=context,
                                                    action_id=new_action_id)

        task_record = self.browse(cr, uid, ids[0], context=context)
        actions = []
        if not task_record.user_id:
            actions.append({'url': take_action, 'title': _('I take it')})
        else:
            actions.append({'url': new_action, 'title': _('New Issue')})

        res['group_project_user'] = {'actions': actions}
        return res

    @api.cr_uid_context
    def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
        """ Override to get the reply_to of the parent project. """
        issues = self.browse(cr, SUPERUSER_ID, ids, context=context)
        project_ids = set(
            [issue.project_id.id for issue in issues if issue.project_id])
        aliases = self.pool['project.project'].message_get_reply_to(
            cr, uid, list(project_ids), default=default, context=context)
        return dict(
            (issue.id,
             aliases.get(issue.project_id and issue.project_id.id or 0, False))
            for issue in issues)

    def message_get_suggested_recipients(self, cr, uid, ids, context=None):
        recipients = super(project_issue,
                           self).message_get_suggested_recipients(
                               cr, uid, ids, context=context)
        try:
            for issue in self.browse(cr, uid, ids, context=context):
                if issue.partner_id:
                    issue._message_add_suggested_recipient(
                        recipients,
                        partner=issue.partner_id,
                        reason=_('Customer'))
                elif issue.email_from:
                    issue._message_add_suggested_recipient(
                        recipients,
                        email=issue.email_from,
                        reason=_('Customer Email'))
        except AccessError:  # no read access rights -> just ignore suggested recipients because this imply modifying followers
            pass
        return recipients

    def email_split(self, cr, uid, ids, msg, context=None):
        email_list = tools.email_split((msg.get('to') or '') + ',' +
                                       (msg.get('cc') or ''))
        # check left-part is not already an alias
        issue_ids = self.browse(cr, uid, ids, context=context)
        aliases = [
            issue.project_id.alias_name for issue in issue_ids
            if issue.project_id
        ]
        return filter(lambda x: x.split('@')[0] not in aliases, email_list)

    def message_new(self, cr, uid, msg, custom_values=None, context=None):
        """ Overrides mail_thread message_new that is called by the mailgateway
            through message_process.
            This override updates the document according to the email.
        """
        if custom_values is None:
            custom_values = {}
        context = dict(context or {}, state_to='draft')
        defaults = {
            'name': msg.get('subject') or _("No Subject"),
            'email_from': msg.get('from'),
            'email_cc': msg.get('cc'),
            'partner_id': msg.get('author_id', False),
            'user_id': False,
        }
        defaults.update(custom_values)

        res_id = super(project_issue, self).message_new(cr,
                                                        uid,
                                                        msg,
                                                        custom_values=defaults,
                                                        context=context)
        email_list = self.email_split(cr, uid, [res_id], msg, context=context)
        partner_ids = self._find_partner_from_emails(cr,
                                                     uid, [res_id],
                                                     email_list,
                                                     force_create=True,
                                                     context=context)
        self.message_subscribe(cr, uid, [res_id], partner_ids, context=context)
        return res_id

    def message_update(self,
                       cr,
                       uid,
                       ids,
                       msg,
                       update_vals=None,
                       context=None):
        """ Override to update the issue according to the email. """

        email_list = self.email_split(cr, uid, ids, msg, context=context)
        partner_ids = self._find_partner_from_emails(cr,
                                                     uid,
                                                     ids,
                                                     email_list,
                                                     force_create=True,
                                                     context=context)
        self.message_subscribe(cr, uid, ids, partner_ids, context=context)
        return super(project_issue,
                     self).message_update(cr,
                                          uid,
                                          ids,
                                          msg,
                                          update_vals=update_vals,
                                          context=context)

    @api.cr_uid_ids_context
    @api.returns('mail.message', lambda value: value.id)
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     subtype=None,
                     context=None,
                     **kwargs):
        """ Overrides mail_thread message_post so that we can set the date of last action field when
            a new message is posted on the issue.
        """
        if context is None:
            context = {}
        res = super(project_issue, self).message_post(cr,
                                                      uid,
                                                      thread_id,
                                                      subtype=subtype,
                                                      context=context,
                                                      **kwargs)
        if thread_id and subtype:
            self.write(cr,
                       SUPERUSER_ID,
                       thread_id, {'date_action_last': fields.datetime.now()},
                       context=context)
        return res
예제 #23
0
파일: procurement.py 프로젝트: LiberTang0/5
class procurement_order(osv.osv):
    """
    Procurement Orders
    """
    _name = "procurement.order"
    _description = "Procurement"
    _order = 'priority desc, date_planned, id asc'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _log_create = False
    _columns = {
        'name':
        fields.text('Description', required=True),
        'origin':
        fields.char(
            'Source Document',
            help="Reference of the document that created this Procurement.\n"
            "This is automatically completed by eCore."),
        'company_id':
        fields.many2one('res.company', 'Company', required=True),

        # These two fields are used for shceduling
        'priority':
        fields.selection(PROCUREMENT_PRIORITIES,
                         'Priority',
                         required=True,
                         select=True,
                         track_visibility='onchange'),
        'date_planned':
        fields.datetime('Scheduled Date',
                        required=True,
                        select=True,
                        track_visibility='onchange'),
        'group_id':
        fields.many2one('procurement.group', 'Procurement Group'),
        'rule_id':
        fields.many2one(
            'procurement.rule',
            'Rule',
            track_visibility='onchange',
            help=
            "Chosen rule for the procurement resolution. Usually chosen by the system but can be manually set by the procurement manager to force an unusual behavior."
        ),
        'product_id':
        fields.many2one('product.product',
                        'Product',
                        required=True,
                        states={'confirmed': [('readonly', False)]},
                        readonly=True),
        'product_qty':
        fields.float(
            'Quantity',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            required=True,
            states={'confirmed': [('readonly', False)]},
            readonly=True),
        'product_uom':
        fields.many2one('product.uom',
                        'Product Unit of Measure',
                        required=True,
                        states={'confirmed': [('readonly', False)]},
                        readonly=True),
        'state':
        fields.selection([('cancel', 'Cancelled'), ('confirmed', 'Confirmed'),
                          ('exception', 'Exception'), ('running', 'Running'),
                          ('done', 'Done')],
                         'Status',
                         required=True,
                         track_visibility='onchange',
                         copy=False),
    }

    _defaults = {
        'state':
        'confirmed',
        'priority':
        '1',
        'date_planned':
        lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
        'company_id':
        lambda self, cr, uid, c: self.pool.get('res.company').
        _company_default_get(cr, uid, 'procurement.order', context=c)
    }

    def _needaction_domain_get(self, cr, uid, context=None):
        return [('state', '=', 'exception')]

    def unlink(self, cr, uid, ids, context=None):
        procurements = self.read(cr, uid, ids, ['state'], context=context)
        unlink_ids = []
        for s in procurements:
            if s['state'] == 'cancel':
                unlink_ids.append(s['id'])
            else:
                raise UserError(
                    _('Cannot delete Procurement Order(s) which are in %s state.'
                      ) % s['state'])
        return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)

    def create(self, cr, uid, vals, context=None):
        context = context or {}
        procurement_id = super(procurement_order, self).create(cr,
                                                               uid,
                                                               vals,
                                                               context=context)
        if not context.get('procurement_autorun_defer'):
            self.run(cr, uid, [procurement_id], context=context)
        return procurement_id

    def do_view_procurements(self, cr, uid, ids, context=None):
        '''
        This function returns an action that display existing procurement orders
        of same procurement group of given ids.
        '''
        act_obj = self.pool.get('ir.actions.act_window')
        action_id = self.pool.get('ir.model.data').xmlid_to_res_id(
            cr,
            uid,
            'procurement.do_view_procurements',
            raise_if_not_found=True)
        result = act_obj.read(cr, uid, [action_id], context=context)[0]
        group_ids = set([
            proc.group_id.id
            for proc in self.browse(cr, uid, ids, context=context)
            if proc.group_id
        ])
        result['domain'] = "[('group_id','in',[" + ','.join(
            map(str, list(group_ids))) + "])]"
        return result

    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
        """ Finds UoM of changed product.
        @param product_id: Changed id of product.
        @return: Dictionary of values.
        """
        if product_id:
            w = self.pool.get('product.product').browse(cr,
                                                        uid,
                                                        product_id,
                                                        context=context)
            v = {
                'product_uom': w.uom_id.id,
            }
            return {'value': v}
        return {}

    def get_cancel_ids(self, cr, uid, ids, context=None):
        return [
            proc.id for proc in self.browse(cr, uid, ids, context=context)
            if proc.state != 'done'
        ]

    def cancel(self, cr, uid, ids, context=None):
        #cancel only the procurements that aren't done already
        to_cancel_ids = self.get_cancel_ids(cr, uid, ids, context=context)
        if to_cancel_ids:
            return self.write(cr,
                              uid,
                              to_cancel_ids, {'state': 'cancel'},
                              context=context)

    def reset_to_confirmed(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': 'confirmed'},
                          context=context)

    @api.v8
    def run(self, autocommit=False):
        return self._model.run(self._cr,
                               self._uid,
                               self.ids,
                               autocommit=False,
                               context=self._context)

    @api.v7
    def run(self, cr, uid, ids, autocommit=False, context=None):
        for procurement_id in ids:
            #we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy
            #and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration
            #will fetch all the ids again)
            procurement = self.browse(cr, uid, procurement_id, context=context)
            if procurement.state not in ("running", "done"):
                try:
                    if self._assign(cr, uid, procurement, context=context):
                        res = self._run(cr,
                                        uid,
                                        procurement,
                                        context=context or {})
                        if res:
                            self.write(cr,
                                       uid, [procurement.id],
                                       {'state': 'running'},
                                       context=context)
                        else:
                            self.write(cr,
                                       uid, [procurement.id],
                                       {'state': 'exception'},
                                       context=context)
                    else:
                        self.message_post(
                            cr,
                            uid, [procurement.id],
                            body=_('No rule matching this procurement'),
                            context=context)
                        self.write(cr,
                                   uid, [procurement.id],
                                   {'state': 'exception'},
                                   context=context)
                    if autocommit:
                        cr.commit()
                except OperationalError:
                    if autocommit:
                        cr.rollback()
                        continue
                    else:
                        raise
        return True

    def check(self, cr, uid, ids, autocommit=False, context=None):
        done_ids = []
        for procurement in self.browse(cr, uid, ids, context=context):
            try:
                result = self._check(cr, uid, procurement, context=context)
                if result:
                    done_ids.append(procurement.id)
                if autocommit:
                    cr.commit()
            except OperationalError:
                if autocommit:
                    cr.rollback()
                    continue
                else:
                    raise
        if done_ids:
            self.write(cr, uid, done_ids, {'state': 'done'}, context=context)
        return done_ids

    #
    # Method to overwrite in different procurement modules
    #
    def _find_suitable_rule(self, cr, uid, procurement, context=None):
        '''This method returns a procurement.rule that depicts what to do with the given procurement
        in order to complete its needs. It returns False if no suiting rule is found.
            :param procurement: browse record
            :rtype: int or False
        '''
        return False

    def _assign(self, cr, uid, procurement, context=None):
        '''This method check what to do with the given procurement in order to complete its needs.
        It returns False if no solution is found, otherwise it stores the matching rule (if any) and
        returns True.
            :param procurement: browse record
            :rtype: boolean
        '''
        #if the procurement already has a rule assigned, we keep it (it has a higher priority as it may have been chosen manually)
        if procurement.rule_id:
            return True
        elif procurement.product_id.type not in ('service', 'digital'):
            rule_id = self._find_suitable_rule(cr,
                                               uid,
                                               procurement,
                                               context=context)
            if rule_id:
                self.write(cr,
                           uid, [procurement.id], {'rule_id': rule_id},
                           context=context)
                return True
        return False

    def _run(self, cr, uid, procurement, context=None):
        '''This method implements the resolution of the given procurement
            :param procurement: browse record
            :returns: True if the resolution of the procurement was a success, False otherwise to set it in exception
        '''
        return True

    def _check(self, cr, uid, procurement, context=None):
        '''Returns True if the given procurement is fulfilled, False otherwise
            :param procurement: browse record
            :rtype: boolean
        '''
        return False

    #
    # Scheduler
    #
    def run_scheduler(self,
                      cr,
                      uid,
                      use_new_cursor=False,
                      company_id=False,
                      context=None):
        '''
        Call the scheduler to check the procurement order. This is intented to be done for all existing companies at
        the same time, so we're running all the methods as SUPERUSER to avoid intercompany and access rights issues.

        @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 use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
            This is appropriate for batch jobs only.
        @param context: A standard dictionary for contextual values
        @return:  Dictionary of values
        '''
        if context is None:
            context = {}
        try:
            if use_new_cursor:
                cr = ecore.registry(cr.dbname).cursor()

            # Run confirmed procurements
            dom = [('state', '=', 'confirmed')]
            if company_id:
                dom += [('company_id', '=', company_id)]
            prev_ids = []
            while True:
                ids = self.search(cr, SUPERUSER_ID, dom, context=context)
                if not ids or prev_ids == ids:
                    break
                else:
                    prev_ids = ids
                self.run(cr,
                         SUPERUSER_ID,
                         ids,
                         autocommit=use_new_cursor,
                         context=context)
                if use_new_cursor:
                    cr.commit()

            # Check if running procurements are done
            offset = 0
            dom = [('state', '=', 'running')]
            if company_id:
                dom += [('company_id', '=', company_id)]
            prev_ids = []
            while True:
                ids = self.search(cr,
                                  SUPERUSER_ID,
                                  dom,
                                  offset=offset,
                                  context=context)
                if not ids or prev_ids == ids:
                    break
                else:
                    prev_ids = ids
                self.check(cr,
                           SUPERUSER_ID,
                           ids,
                           autocommit=use_new_cursor,
                           context=context)
                if use_new_cursor:
                    cr.commit()

        finally:
            if use_new_cursor:
                try:
                    cr.close()
                except Exception:
                    pass

        return {}
예제 #24
0
class hr_department(osv.osv):
    _name = "hr.department"
    _description = "HR Department"
    _inherit = ['mail.thread', 'ir.needaction_mixin']

    def _dept_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 = {
        'name':
        fields.char('Department Name', required=True),
        'complete_name':
        fields.function(_dept_name_get_fnc, type="char", string='Name'),
        'company_id':
        fields.many2one('res.company', 'Company', select=True, required=False),
        'parent_id':
        fields.many2one('hr.department', 'Parent Department', select=True),
        'child_ids':
        fields.one2many('hr.department', 'parent_id', 'Child Departments'),
        'manager_id':
        fields.many2one('hr.employee', 'Manager', track_visibility='onchange'),
        'member_ids':
        fields.one2many('hr.employee',
                        'department_id',
                        'Members',
                        readonly=True),
        'jobs_ids':
        fields.one2many('hr.job', 'department_id', 'Jobs'),
        'note':
        fields.text('Note'),
        'color':
        fields.integer('Color Index'),
    }

    _defaults = {
        'company_id':
        lambda self, cr, uid, c: self.pool.get('res.company').
        _company_default_get(cr, uid, 'hr.department', context=c),
    }

    _constraints = [(osv.osv._check_recursion,
                     _('Error! You cannot create recursive departments.'),
                     ['parent_id'])]

    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        if isinstance(ids, (int, long)):
            ids = [ids]
        if context is None:
            context = {}
        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

    def create(self, cr, uid, vals, context=None):
        if context is None:
            context = {}
        context['mail_create_nosubscribe'] = True
        # TDE note: auto-subscription of manager done by hand, because currently
        # the tracking allows to track+subscribe fields linked to a res.user record
        # An update of the limited behavior should come, but not currently done.
        manager_id = vals.get("manager_id")
        new_id = super(hr_department, self).create(cr,
                                                   uid,
                                                   vals,
                                                   context=context)
        if manager_id:
            employee = self.pool.get('hr.employee').browse(cr,
                                                           uid,
                                                           manager_id,
                                                           context=context)
            if employee.user_id:
                self.message_subscribe_users(cr,
                                             uid, [new_id],
                                             user_ids=[employee.user_id.id],
                                             context=context)
        return new_id

    def write(self, cr, uid, ids, vals, context=None):
        # TDE note: auto-subscription of manager done by hand, because currently
        # the tracking allows to track+subscribe fields linked to a res.user record
        # An update of the limited behavior should come, but not currently done.
        if isinstance(ids, (int, long)):
            ids = [ids]
        employee_ids = []
        if 'manager_id' in vals:
            manager_id = vals.get("manager_id")
            if manager_id:
                employee = self.pool['hr.employee'].browse(cr,
                                                           uid,
                                                           manager_id,
                                                           context=context)
                if employee.user_id:
                    self.message_subscribe_users(
                        cr,
                        uid,
                        ids,
                        user_ids=[employee.user_id.id],
                        context=context)
            for department in self.browse(cr, uid, ids, context=context):
                employee_ids += self.pool['hr.employee'].search(
                    cr,
                    uid, [('id', '!=', manager_id),
                          ('department_id', '=', department.id),
                          ('parent_id', '=', department.manager_id.id)],
                    context=context)
            self.pool['hr.employee'].write(cr,
                                           uid,
                                           employee_ids,
                                           {'parent_id': manager_id},
                                           context=context)
        return super(hr_department, self).write(cr,
                                                uid,
                                                ids,
                                                vals,
                                                context=context)
예제 #25
0
def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context):
    """ just so CreatorCase.export can be used
    """
    pass

models = [
    ('boolean', fields.boolean()),
    ('integer', fields.integer()),
    ('float', fields.float()),
    ('decimal', fields.float(digits=(16, 3))),
    ('string.bounded', fields.char('unknown', size=16)),
    ('string.required', fields.char('unknown', size=None, required=True)),
    ('string', fields.char('unknown', size=None)),
    ('date', fields.date()),
    ('datetime', fields.datetime()),
    ('text', fields.text()),
    ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])),
    # here use size=-1 to store the values as integers instead of strings
    ('selection.function', fields.selection(selection_fn, size=-1)),
    # just relate to an integer
    ('many2one', fields.many2one('export.integer')),
    ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
    ('many2many', fields.many2many('export.many2many.other')),
    ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")),
    # related: specialization of fields.function, should work the same way
    # TODO: reference
]

for name, field in models:
    class NewModel(orm.Model):
        _name = 'export.%s' % name
예제 #26
0
파일: ir_filters.py 프로젝트: LiberTang0/5
class ir_filters(osv.osv):
    _name = 'ir.filters'
    _description = 'Filters'

    def _list_all_models(self, cr, uid, context=None):
        cr.execute("SELECT model, name FROM ir_model ORDER BY name")
        return cr.fetchall()

    def copy(self, cr, uid, id, default=None, context=None):
        name = self.read(cr, uid, [id], ['name'])[0]['name']
        default.update({'name':_('%s (copy)') % name})
        return super(ir_filters, self).copy(cr, uid, id, default, context)

    def _get_action_domain(self, cr, uid, action_id=None):
        """Return a domain component for matching filters that are visible in the
           same context (menu/view) as the given action."""
        if action_id:
            # filters specific to this menu + global ones
            return [('action_id', 'in' , [action_id, False])]
        # only global ones
        return [('action_id', '=', False)]

    def get_filters(self, cr, uid, model, action_id=None, context=None):
        """Obtain the list of filters available for the user on the given model.

        :param action_id: optional ID of action to restrict filters to this action
            plus global filters. If missing only global filters are returned.
            The action does not have to correspond to the model, it may only be
            a contextual action.
        :return: list of :meth:`~osv.read`-like dicts containing the
            ``name``, ``is_default``, ``domain``, ``user_id`` (m2o tuple),
            ``action_id`` (m2o tuple) and ``context`` of the matching ``ir.filters``.
        """
        # available filters: private filters (user_id=uid) and public filters (uid=NULL),
        # and filters for the action (action_id=action_id) or global (action_id=NULL)
        context = self.pool['res.users'].context_get(cr, uid)
        action_domain = self._get_action_domain(cr, uid, action_id)
        filter_ids = self.search(cr, uid, action_domain +
            [('model_id','=',model),('user_id','in',[uid, False])])
        my_filters = self.read(cr, uid, filter_ids,
            ['name', 'is_default', 'domain', 'context', 'user_id', 'sort'], context=context)
        return my_filters

    def _check_global_default(self, cr, uid, vals, matching_filters, context=None):
        """ _check_global_default(cursor, UID, dict, list(dict), dict) -> None

        Checks if there is a global default for the model_id requested.

        If there is, and the default is different than the record being written
        (-> we're not updating the current global default), raise an error
        to avoid users unknowingly overwriting existing global defaults (they
        have to explicitly remove the current default before setting a new one)

        This method should only be called if ``vals`` is trying to set
        ``is_default``

        :raises ecore.exceptions.Warning: if there is an existing default and
                                            we're not updating it
        """
        action_domain = self._get_action_domain(cr, uid, vals.get('action_id'))
        existing_default = self.search(cr, uid, action_domain + [
            ('model_id', '=', vals['model_id']),
            ('user_id', '=', False),
            ('is_default', '=', True)], context=context)

        if not existing_default: return
        if matching_filters and \
           (matching_filters[0]['id'] == existing_default[0]):
            return

        raise exceptions.Warning(
            _("There is already a shared filter set as default for %(model)s, delete or change it before setting a new default") % {
                'model': vals['model_id']
            })

    def create_or_replace(self, cr, uid, vals, context=None):
        lower_name = vals['name'].lower()
        action_id = vals.get('action_id')
        current_filters = self.get_filters(cr, uid, vals['model_id'], action_id)
        matching_filters = [f for f in current_filters
                                if f['name'].lower() == lower_name
                                # next line looks for matching user_ids (specific or global), i.e.
                                # f.user_id is False and vals.user_id is False or missing,
                                # or f.user_id.id == vals.user_id
                                if (f['user_id'] and f['user_id'][0]) == vals.get('user_id', False)]

        if vals.get('is_default'):
            if vals.get('user_id'):
                # Setting new default: any other default that belongs to the user
                # should be turned off
                action_domain = self._get_action_domain(cr, uid, action_id)
                act_ids = self.search(cr, uid, action_domain + [
                        ('model_id', '=', vals['model_id']),
                        ('user_id', '=', vals['user_id']),
                        ('is_default', '=', True),
                    ], context=context)
                if act_ids:
                    self.write(cr, uid, act_ids, {'is_default': False}, context=context)
            else:
                self._check_global_default(
                    cr, uid, vals, matching_filters, context=None)

        # When a filter exists for the same (name, model, user) triple, we simply
        # replace its definition (considering action_id irrelevant here)
        if matching_filters:
            self.write(cr, uid, matching_filters[0]['id'], vals, context)
            return matching_filters[0]['id']

        return self.create(cr, uid, vals, context)

    _sql_constraints = [
        # Partial constraint, complemented by unique index (see below)
        # Still useful to keep because it provides a proper error message when a violation
        # occurs, as it shares the same prefix as the unique index. 
        ('name_model_uid_unique', 'unique (name, model_id, user_id, action_id)', 'Filter names must be unique'),
    ]

    def _auto_init(self, cr, context=None):
        result = super(ir_filters, self)._auto_init(cr, context)
        # Use unique index to implement unique constraint on the lowercase name (not possible using a constraint)
        cr.execute("DROP INDEX IF EXISTS ir_filters_name_model_uid_unique_index") # drop old index w/o action
        cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = 'ir_filters_name_model_uid_unique_action_index'")
        if not cr.fetchone():
            cr.execute("""CREATE UNIQUE INDEX "ir_filters_name_model_uid_unique_action_index" ON ir_filters
                            (lower(name), model_id, COALESCE(user_id,-1), COALESCE(action_id,-1))""")
        return result

    _columns = {
        'name': fields.char('Filter Name', translate=True, required=True),
        'user_id': fields.many2one('res.users', 'User', ondelete='cascade',
                                   help="The user this filter is private to. When left empty the filter is public "
                                        "and available to all users."),
        'domain': fields.text('Domain', required=True),
        'context': fields.text('Context', required=True),
        'sort': fields.text('Sort', required=True),
        'model_id': fields.selection(_list_all_models, 'Model', required=True),
        'is_default': fields.boolean('Default filter'),
        'action_id': fields.many2one('ir.actions.actions', 'Action', ondelete='cascade',
                                     help="The menu action this filter applies to. "
                                          "When left empty the filter applies to all menus "
                                          "for this model."),
        'active': fields.boolean('Active')
    }
    _defaults = {
        'domain': '[]',
        'context':'{}',
        'sort': '[]',
        'user_id': lambda self,cr,uid,context=None: uid,
        'is_default': False,
        'active': True
    }
    _order = 'model_id, name, id desc'
예제 #27
0
class payroll_advice(osv.osv):
    '''
    Bank Advice
    '''
    _name = 'hr.payroll.advice'
    _description = 'Bank Advice'
    _columns = {
        'name':
        fields.char(
            'Name',
            readonly=True,
            required=True,
            states={'draft': [('readonly', False)]},
        ),
        'note':
        fields.text('Description'),
        'date':
        fields.date('Date',
                    readonly=True,
                    required=True,
                    states={'draft': [('readonly', False)]},
                    help="Advice Date is used to search Payslips"),
        'state':
        fields.selection([
            ('draft', 'Draft'),
            ('confirm', 'Confirmed'),
            ('cancel', 'Cancelled'),
        ],
                         'Status',
                         select=True,
                         readonly=True),
        'number':
        fields.char('Reference', readonly=True),
        'line_ids':
        fields.one2many('hr.payroll.advice.line',
                        'advice_id',
                        'Employee Salary',
                        states={'draft': [('readonly', False)]},
                        readonly=True,
                        copy=True),
        'chaque_nos':
        fields.char('Cheque Numbers'),
        'neft':
        fields.boolean(
            'NEFT Transaction',
            help="Check this box if your company use online transfer for salary"
        ),
        'company_id':
        fields.many2one('res.company',
                        'Company',
                        required=True,
                        readonly=True,
                        states={'draft': [('readonly', False)]}),
        'bank_id':
        fields.many2one(
            'res.bank',
            'Bank',
            readonly=True,
            states={'draft': [('readonly', False)]
                    },
            help="Select the Bank from which the salary is going to be paid"),
        'batch_id':
        fields.many2one('hr.payslip.run', 'Batch', readonly=True)
    }

    _defaults = {
        'date': lambda * a: time.strftime('%Y-%m-%d'),
        'state': lambda * a: 'draft',
        'company_id': lambda self, cr, uid, context: \
                self.pool.get('res.users').browse(cr, uid, uid,
                    context=context).company_id.id,
        'note': "Please make the payroll transfer from above account number to the below mentioned account numbers towards employee salaries:"
    }

    def compute_advice(self, cr, uid, ids, context=None):
        """
        Advice - Create Advice lines in Payment Advice and
        compute Advice lines.
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of Advice’s IDs
        @return: Advice lines
        @param context: A standard dictionary for contextual values
        """
        payslip_pool = self.pool.get('hr.payslip')
        advice_line_pool = self.pool.get('hr.payroll.advice.line')
        payslip_line_pool = self.pool.get('hr.payslip.line')

        for advice in self.browse(cr, uid, ids, context=context):
            old_line_ids = advice_line_pool.search(
                cr, uid, [('advice_id', '=', advice.id)], context=context)
            if old_line_ids:
                advice_line_pool.unlink(cr, uid, old_line_ids, context=context)
            slip_ids = payslip_pool.search(cr,
                                           uid,
                                           [('date_from', '<=', advice.date),
                                            ('date_to', '>=', advice.date),
                                            ('state', '=', 'done')],
                                           context=context)
            for slip in payslip_pool.browse(cr, uid, slip_ids,
                                            context=context):
                if not slip.employee_id.bank_account_id and not slip.employee_id.bank_account_id.acc_number:
                    raise UserError(
                        _('Please define bank account for the %s employee') %
                        (slip.employee_id.name, ))
                line_ids = payslip_line_pool.search(cr,
                                                    uid,
                                                    [('slip_id', '=', slip.id),
                                                     ('code', '=', 'NET')],
                                                    context=context)
                if line_ids:
                    line = payslip_line_pool.browse(cr,
                                                    uid,
                                                    line_ids,
                                                    context=context)[0]
                    advice_line = {
                        'advice_id': advice.id,
                        'name': slip.employee_id.bank_account_id.acc_number,
                        'employee_id': slip.employee_id.id,
                        'bysal': line.total
                    }
                    advice_line_pool.create(cr,
                                            uid,
                                            advice_line,
                                            context=context)
                payslip_pool.write(cr,
                                   uid,
                                   slip_ids, {'advice_id': advice.id},
                                   context=context)
        return True

    def confirm_sheet(self, cr, uid, ids, context=None):
        """
        confirm Advice - confirmed Advice after computing Advice Lines..
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of confirm Advice’s IDs
        @return: confirmed Advice lines and set sequence of Advice.
        @param context: A standard dictionary for contextual values
        """
        seq_obj = self.pool.get('ir.sequence')
        for advice in self.browse(cr, uid, ids, context=context):
            if not advice.line_ids:
                raise UserError(
                    _('You can not confirm Payment advice without advice lines.'
                      ))
            advice_date = datetime.strptime(advice.date, DATETIME_FORMAT)
            advice_year = advice_date.strftime(
                '%m') + '-' + advice_date.strftime('%Y')
            number = seq_obj.next_by_code(cr, uid, 'payment.advice')
            sequence_num = 'PAY' + '/' + advice_year + '/' + number
            self.write(cr,
                       uid, [advice.id], {
                           'number': sequence_num,
                           'state': 'confirm'
                       },
                       context=context)
        return True

    def set_to_draft(self, cr, uid, ids, context=None):
        """Resets Advice as draft.
        """
        return self.write(cr, uid, ids, {'state': 'draft'}, context=context)

    def cancel_sheet(self, cr, uid, ids, context=None):
        """Marks Advice as cancelled.
        """
        return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)

    def onchange_company_id(self,
                            cr,
                            uid,
                            ids,
                            company_id=False,
                            context=None):
        res = {}
        if company_id:
            company = self.pool.get('res.company').browse(cr,
                                                          uid, [company_id],
                                                          context=context)[0]
            if company.partner_id.bank_ids:
                res.update(
                    {'bank_id': company.partner_id.bank_ids[0].bank_id.id})
        return {'value': res}
예제 #28
0
class base_module_upgrade(osv.osv_memory):
    """ Module Upgrade """

    _name = "base.module.upgrade"
    _description = "Module Upgrade"

    _columns = {
        'module_info': fields.text('Apps to Update', readonly=True),
    }

    def fields_view_get(self,
                        cr,
                        uid,
                        view_id=None,
                        view_type='form',
                        context=None,
                        toolbar=False,
                        submenu=False):
        res = super(base_module_upgrade,
                    self).fields_view_get(cr,
                                          uid,
                                          view_id=view_id,
                                          view_type=view_type,
                                          context=context,
                                          toolbar=toolbar,
                                          submenu=False)
        if view_type != 'form':
            return res

        context = {} if context is None else context
        record_id = context and context.get('active_id', False) or False
        active_model = context.get('active_model')
        if (not record_id) or (not active_model):
            return res

        ids = self.get_module_list(cr, uid, context=context)
        if not ids:
            res['arch'] = '''<form string="Upgrade Completed" version="7.0">
                                <separator string="Upgrade Completed" colspan="4"/>
                                <footer>
                                    <button name="config" string="Start Configuration" type="object" class="btn-primary"/>
                                    <button special="cancel" string="Close" class="btn-default"/>
                                </footer>
                             </form>'''

        return res

    def get_module_list(self, cr, uid, context=None):
        mod_obj = self.pool.get('ir.module.module')
        ids = mod_obj.search(
            cr, uid,
            [('state', 'in', ['to upgrade', 'to remove', 'to install'])])
        return ids

    def default_get(self, cr, uid, fields, context=None):
        mod_obj = self.pool.get('ir.module.module')
        ids = self.get_module_list(cr, uid, context=context)
        res = mod_obj.read(cr, uid, ids, ['name', 'state'], context)
        return {
            'module_info':
            '\n'.join(map(lambda x: x['name'] + ' : ' + x['state'], res))
        }

    def upgrade_module_cancel(self, cr, uid, ids, context=None):
        mod_obj = self.pool.get('ir.module.module')
        to_installed_ids = mod_obj.search(
            cr, uid, [('state', 'in', ['to upgrade', 'to remove'])])
        if to_installed_ids:
            mod_obj.write(cr,
                          uid,
                          to_installed_ids, {'state': 'installed'},
                          context=context)

        to_uninstalled_ids = mod_obj.search(cr, uid,
                                            [('state', '=', 'to install')])
        if to_uninstalled_ids:
            mod_obj.write(cr,
                          uid,
                          to_uninstalled_ids, {'state': 'uninstalled'},
                          context=context)

        return {'type': 'ir.actions.act_window_close'}

    def upgrade_module(self, cr, uid, ids, context=None):
        ir_module = self.pool.get('ir.module.module')

        # install/upgrade: double-check preconditions
        ids = ir_module.search(cr, uid,
                               [('state', 'in', ['to upgrade', 'to install'])])
        if ids:
            cr.execute(
                """SELECT d.name FROM ir_module_module m
                                        JOIN ir_module_module_dependency d ON (m.id = d.module_id)
                                        LEFT JOIN ir_module_module m2 ON (d.name = m2.name)
                          WHERE m.id in %s and (m2.state IS NULL or m2.state IN %s)""",
                (tuple(ids), ('uninstalled', )))
            unmet_packages = [x[0] for x in cr.fetchall()]
            if unmet_packages:
                raise UserError(
                    _('Following modules are not installed or unknown: %s') %
                    ('\n\n' + '\n'.join(unmet_packages)))

            ir_module.download(cr, uid, ids, context=context)
            cr.commit()  # save before re-creating cursor below

        ecore.api.Environment.reset()
        ecore.modules.registry.RegistryManager.new(cr.dbname,
                                                   update_module=True)

        return {'type': 'ir.actions.act_window_close'}

    def config(self, cr, uid, ids, context=None):
        return self.pool.get('res.config').next(cr, uid, [], context=context)
예제 #29
0
파일: res_company.py 프로젝트: LiberTang0/5
class res_company(osv.osv):
    _name = "res.company"
    _description = 'Companies'
    _order = 'name'

    def _get_address_data(self, cr, uid, ids, field_names, arg, context=None):
        """ Read the 'address' functional fields. """
        result = {}
        part_obj = self.pool.get('res.partner')
        for company in self.browse(cr, uid, ids, context=context):
            result[company.id] = {}.fromkeys(field_names, False)
            if company.partner_id:
                address_data = part_obj.address_get(cr,
                                                    ecore.SUPERUSER_ID,
                                                    [company.partner_id.id],
                                                    adr_pref=['contact'])
                if address_data['contact']:
                    address = part_obj.read(cr,
                                            ecore.SUPERUSER_ID,
                                            [address_data['contact']],
                                            field_names,
                                            context=context)[0]
                    for field in field_names:
                        result[company.id][field] = address[field] or False
        return result

    def _set_address_data(self,
                          cr,
                          uid,
                          company_id,
                          name,
                          value,
                          arg,
                          context=None):
        """ Write the 'address' functional fields. """
        company = self.browse(cr, uid, company_id, context=context)
        if company.partner_id:
            part_obj = self.pool.get('res.partner')
            address_data = part_obj.address_get(cr,
                                                uid, [company.partner_id.id],
                                                adr_pref=['contact'])
            address = address_data['contact']
            if address:
                part_obj.write(cr,
                               uid, [address], {name: value or False},
                               context=context)
            else:
                part_obj.create(cr,
                                uid, {
                                    name: value or False,
                                    'parent_id': company.partner_id.id
                                },
                                context=context)
        return True

    def _get_logo_web(self, cr, uid, ids, _field_name, _args, context=None):
        result = dict.fromkeys(ids, False)
        for record in self.browse(cr, uid, ids, context=context):
            size = (180, None)
            result[record.id] = image_resize_image(record.partner_id.image,
                                                   size)
        return result

    def _get_companies_from_partner(self, cr, uid, ids, context=None):
        return self.pool['res.company'].search(cr,
                                               uid,
                                               [('partner_id', 'in', ids)],
                                               context=context)

    _columns = {
        'name':
        fields.related('partner_id',
                       'name',
                       string='Company Name',
                       size=128,
                       required=True,
                       store=True,
                       type='char'),
        'parent_id':
        fields.many2one('res.company', 'Parent Company', select=True),
        'child_ids':
        fields.one2many('res.company', 'parent_id', 'Child Companies'),
        'partner_id':
        fields.many2one('res.partner', 'Partner', required=True),
        'rml_header':
        fields.text('RML Header', required=True),
        'rml_header1':
        fields.char(
            'Company Tagline',
            help=
            "Appears by default on the top right corner of your printed documents (report header)."
        ),
        'rml_header2':
        fields.text('RML Internal Header', required=True),
        'rml_header3':
        fields.text('RML Internal Header for Landscape Reports',
                    required=True),
        'rml_footer':
        fields.text(
            'Report Footer',
            help="Footer text displayed at the bottom of all reports."),
        'rml_footer_readonly':
        fields.related('rml_footer',
                       type='text',
                       string='Report Footer',
                       readonly=True),
        'custom_footer':
        fields.boolean(
            'Custom Footer',
            help=
            "Check this to define the report footer manually.  Otherwise it will be filled in automatically."
        ),
        'font':
        fields.many2one(
            'res.font',
            string="Font",
            domain=[('mode', 'in', ('Normal', 'Regular', 'all', 'Book'))],
            help=
            "Set the font into the report header, it will be used as default font in the RML reports of the user company"
        ),
        'logo':
        fields.related('partner_id', 'image', string="Logo", type="binary"),
        # logo_web: do not store in attachments, since the image is retrieved in SQL for
        # performance reasons (see addons/web/controllers/main.py, Binary.company_logo)
        'logo_web':
        fields.function(_get_logo_web,
                        string="Logo Web",
                        type="binary",
                        store={
                            'res.company':
                            (lambda s, c, u, i, x: i, ['partner_id'], 10),
                            'res.partner':
                            (_get_companies_from_partner, ['image'], 10),
                        }),
        'currency_id':
        fields.many2one('res.currency', 'Currency', required=True),
        'user_ids':
        fields.many2many('res.users', 'res_company_users_rel', 'cid',
                         'user_id', 'Accepted Users'),
        'account_no':
        fields.char('Account No.'),
        'street':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        size=128,
                        type='char',
                        string="Street",
                        multi='address'),
        'street2':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        size=128,
                        type='char',
                        string="Street2",
                        multi='address'),
        'zip':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        size=24,
                        type='char',
                        string="Zip",
                        multi='address'),
        'city':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        size=24,
                        type='char',
                        string="City",
                        multi='address'),
        'state_id':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        type='many2one',
                        relation='res.country.state',
                        string="Fed. State",
                        multi='address'),
        'country_id':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        type='many2one',
                        relation='res.country',
                        string="Country",
                        multi='address'),
        'email':
        fields.related('partner_id',
                       'email',
                       size=64,
                       type='char',
                       string="Email",
                       store=True),
        'phone':
        fields.related('partner_id',
                       'phone',
                       size=64,
                       type='char',
                       string="Phone",
                       store=True),
        'fax':
        fields.function(_get_address_data,
                        fnct_inv=_set_address_data,
                        size=64,
                        type='char',
                        string="Fax",
                        multi='address'),
        'website':
        fields.related('partner_id',
                       'website',
                       string="Website",
                       type="char",
                       size=64),
        'vat':
        fields.related('partner_id',
                       'vat',
                       string="Tax ID",
                       type="char",
                       size=32),
        'company_registry':
        fields.char('Company Registry', size=64),
        'rml_paper_format':
        fields.selection([('a4', 'A4'), ('us_letter', 'US Letter')],
                         "Paper Format",
                         required=True,
                         oldname='paper_format'),
    }
    _sql_constraints = [('name_uniq', 'unique (name)',
                         'The company name must be unique !')]

    @api.onchange('custom_footer', 'phone', 'fax', 'email', 'website', 'vat',
                  'company_registry')
    def onchange_footer(self):
        if not self.custom_footer:
            # first line (notice that missing elements are filtered out before the join)
            res = ' | '.join(
                filter(bool, [
                    self.phone and '%s: %s' % (_('Phone'), self.phone),
                    self.fax and '%s: %s' % (_('Fax'), self.fax),
                    self.email and '%s: %s' % (_('Email'), self.email),
                    self.website and '%s: %s' % (_('Website'), self.website),
                    self.vat and '%s: %s' % (_('TIN'), self.vat),
                    self.company_registry and '%s: %s' %
                    (_('Reg'), self.company_registry),
                ]))
            self.rml_footer_readonly = res
            self.rml_footer = res

    def onchange_state(self, cr, uid, ids, state_id, context=None):
        if state_id:
            return {
                'value': {
                    'country_id':
                    self.pool.get('res.country.state').browse(
                        cr, uid, state_id, context).country_id.id
                }
            }
        return {}

    def onchange_font_name(self,
                           cr,
                           uid,
                           ids,
                           font,
                           rml_header,
                           rml_header2,
                           rml_header3,
                           context=None):
        """ To change default header style of all <para> and drawstring. """
        def _change_header(header, font):
            """ Replace default fontname use in header and setfont tag """

            default_para = re.sub('fontName.?=.?".*"', 'fontName="%s"' % font,
                                  header)
            return re.sub('(<setFont.?name.?=.?)(".*?")(.)',
                          '\g<1>"%s"\g<3>' % font, default_para)

        if not font:
            return True
        fontname = self.pool.get('res.font').browse(cr,
                                                    uid,
                                                    font,
                                                    context=context).name
        return {
            'value': {
                'rml_header': _change_header(rml_header, fontname),
                'rml_header2': _change_header(rml_header2, fontname),
                'rml_header3': _change_header(rml_header3, fontname)
            }
        }

    def on_change_country(self, cr, uid, ids, country_id, context=None):
        res = {'domain': {'state_id': []}}
        currency_id = self._get_euro(cr, uid, context=context)
        if country_id:
            currency_id = self.pool.get('res.country').browse(
                cr, uid, country_id, context=context).currency_id.id
            res['domain'] = {'state_id': [('country_id', '=', country_id)]}
        res['value'] = {'currency_id': currency_id}
        return res

    def name_search(self,
                    cr,
                    uid,
                    name='',
                    args=None,
                    operator='ilike',
                    context=None,
                    limit=100):
        context = dict(context or {})
        if context.pop('user_preference', None):
            # We browse as superuser. Otherwise, the user would be able to
            # select only the currently visible companies (according to rules,
            # which are probably to allow to see the child companies) even if
            # she belongs to some other companies.
            user = self.pool.get('res.users').browse(cr,
                                                     SUPERUSER_ID,
                                                     uid,
                                                     context=context)
            cmp_ids = list(
                set([user.company_id.id] +
                    [cmp.id for cmp in user.company_ids]))
            uid = SUPERUSER_ID
            args = (args or []) + [('id', 'in', cmp_ids)]
        return super(res_company, self).name_search(cr,
                                                    uid,
                                                    name=name,
                                                    args=args,
                                                    operator=operator,
                                                    context=context,
                                                    limit=limit)

    @api.returns('self')
    def _company_default_get(self,
                             cr,
                             uid,
                             object=False,
                             field=False,
                             context=None):
        """
        Returns the default company (the user's company)
        The 'object' and 'field' arguments are ignored but left here for
        backward compatibility and potential override.
        """
        return self.pool['res.users']._get_company(cr, uid, context=context)

    @tools.ormcache('uid', 'company')
    def _get_company_children(self, cr, uid=None, company=None):
        if not company:
            return []
        ids = self.search(cr, uid, [('parent_id', 'child_of', [company])])
        return ids

    def _get_partner_hierarchy(self, cr, uid, company_id, context=None):
        if company_id:
            parent_id = self.browse(cr, uid, company_id)['parent_id']
            if parent_id:
                return self._get_partner_hierarchy(cr, uid, parent_id.id,
                                                   context)
            else:
                return self._get_partner_descendance(cr, uid, company_id, [],
                                                     context)
        return []

    def _get_partner_descendance(self,
                                 cr,
                                 uid,
                                 company_id,
                                 descendance,
                                 context=None):
        descendance.append(self.browse(cr, uid, company_id).partner_id.id)
        for child_id in self._get_company_children(cr, uid, company_id):
            if child_id != company_id:
                descendance = self._get_partner_descendance(
                    cr, uid, child_id, descendance)
        return descendance

    #
    # This function restart the cache on the _get_company_children method
    #
    def cache_restart(self, cr):
        self._get_company_children.clear_cache(self)

    def create(self, cr, uid, vals, context=None):
        if not vals.get('name', False) or vals.get('partner_id', False):
            self.cache_restart(cr)
            return super(res_company, self).create(cr,
                                                   uid,
                                                   vals,
                                                   context=context)
        obj_partner = self.pool.get('res.partner')
        partner_id = obj_partner.create(cr,
                                        uid, {
                                            'name': vals['name'],
                                            'is_company': True,
                                            'image': vals.get('logo', False)
                                        },
                                        context=context)
        vals.update({'partner_id': partner_id})
        self.cache_restart(cr)
        company_id = super(res_company, self).create(cr,
                                                     uid,
                                                     vals,
                                                     context=context)
        obj_partner.write(cr,
                          uid, [partner_id], {'company_id': company_id},
                          context=context)
        return company_id

    def write(self, cr, uid, ids, values, context=None):
        self.cache_restart(cr)
        return super(res_company, self).write(cr,
                                              uid,
                                              ids,
                                              values,
                                              context=context)

    def _get_euro(self, cr, uid, context=None):
        rate_obj = self.pool.get('res.currency.rate')
        rate_id = rate_obj.search(cr, uid, [('rate', '=', 1)], context=context)
        return rate_id and rate_obj.browse(
            cr, uid, rate_id[0], context=context).currency_id.id or False

    def _get_logo(self, cr, uid, ids):
        return open(
            os.path.join(tools.config['root_path'], 'addons', 'base', 'res',
                         'res_company_logo.png'), 'rb').read().encode('base64')

    def _get_font(self, cr, uid, ids):
        font_obj = self.pool.get('res.font')
        res = font_obj.search(cr,
                              uid, [('family', '=', 'Helvetica'),
                                    ('mode', '=', 'all')],
                              limit=1)
        return res and res[0] or False

    _header = """
<header>
<pageTemplate>
    <frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/>
    <stylesheet>
       <!-- Set here the default font to use for all <para> tags -->
       <paraStyle name='Normal' fontName="DejaVuSans"/>
    </stylesheet>
    <pageGraphics>
        <fill color="black"/>
        <stroke color="black"/>
        <setFont name="DejaVuSans" size="8"/>
        <drawString x="%s" y="%s"> [[ formatLang(time.strftime("%%Y-%%m-%%d"), date=True) ]]  [[ time.strftime("%%H:%%M") ]]</drawString>
        <setFont name="DejaVuSans-Bold" size="10"/>
        <drawCentredString x="%s" y="%s">[[ company.partner_id.name ]]</drawCentredString>
        <stroke color="#000000"/>
        <lines>%s</lines>
        <!-- Set here the default font to use for all <drawString> tags -->
        <!-- don't forget to change the 2 other occurence of <setFont> above if needed --> 
        <setFont name="DejaVuSans" size="8"/>
    </pageGraphics>
</pageTemplate>
</header>"""

    _header2 = _header % (539, 772, "1.0cm", "28.3cm", "11.1cm", "28.3cm",
                          "1.0cm 28.1cm 20.1cm 28.1cm")

    _header3 = _header % (786, 525, 25, 555, 440, 555, "25 550 818 550")

    def _get_header(self, cr, uid, ids):
        try:
            header_file = tools.file_open(
                os.path.join('base', 'report', 'corporate_rml_header.rml'))
            try:
                return header_file.read()
            finally:
                header_file.close()
        except:
            return self._header_a4

    _header_main = """
<header>
    <pageTemplate>
        <frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/>
         <stylesheet>
            <!-- Set here the default font to use for all <para> tags -->
            <paraStyle name='Normal' fontName="DejaVuSans"/>
            <paraStyle name="main_footer" fontSize="8.0" alignment="CENTER"/>
            <paraStyle name="main_header" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
         </stylesheet>
        <pageGraphics>
            <!-- Set here the default font to use for all <drawString> tags -->
            <setFont name="DejaVuSans" size="8"/>
            <!-- You Logo - Change X,Y,Width and Height -->
            <image x="1.3cm" y="%s" height="40.0" >[[ company.logo or removeParentNode('image') ]]</image>
            <fill color="black"/>
            <stroke color="black"/>

            <!-- page header -->
            <lines>1.3cm %s 20cm %s</lines>
            <drawRightString x="20cm" y="%s">[[ company.rml_header1 ]]</drawRightString>
            <drawString x="1.3cm" y="%s">[[ company.partner_id.name ]]</drawString>
            <place x="1.3cm" y="%s" height="1.8cm" width="15.0cm">
                <para style="main_header">[[ display_address(company.partner_id) or  '' ]]</para>
            </place>
            <drawString x="1.3cm" y="%s">Phone:</drawString>
            <drawRightString x="7cm" y="%s">[[ company.partner_id.phone or '' ]]</drawRightString>
            <drawString x="1.3cm" y="%s">Mail:</drawString>
            <drawRightString x="7cm" y="%s">[[ company.partner_id.email or '' ]]</drawRightString>
            <lines>1.3cm %s 7cm %s</lines>

            <!-- left margin -->
            <rotate degrees="90"/>
            <fill color="grey"/>
            <drawString x="2.65cm" y="-0.4cm">generated by eCore.com</drawString>
            <fill color="black"/>
            <rotate degrees="-90"/>

            <!--page bottom-->
            <lines>1.2cm 2.65cm 19.9cm 2.65cm</lines>
            <place x="1.3cm" y="0cm" height="2.55cm" width="19.0cm">
                <para style="main_footer">[[ company.rml_footer ]]</para>
                <para style="main_footer">Contact : [[ user.name ]] - Page: <pageNumber/></para>
            </place>
        </pageGraphics>
    </pageTemplate>
</header>"""

    _header_a4 = _header_main % (
        '21.7cm', '27.7cm', '27.7cm', '27.7cm', '27.8cm', '27.3cm', '25.3cm',
        '25.0cm', '25.0cm', '24.6cm', '24.6cm', '24.5cm', '24.5cm')
    _header_letter = _header_main % (
        '20cm', '26.0cm', '26.0cm', '26.0cm', '26.1cm', '25.6cm', '23.6cm',
        '23.3cm', '23.3cm', '22.9cm', '22.9cm', '22.8cm', '22.8cm')

    def onchange_rml_paper_format(self,
                                  cr,
                                  uid,
                                  ids,
                                  rml_paper_format,
                                  context=None):
        if rml_paper_format == 'us_letter':
            return {'value': {'rml_header': self._header_letter}}
        return {'value': {'rml_header': self._header_a4}}

    def act_discover_fonts(self, cr, uid, ids, context=None):
        return self.pool.get("res.font").font_scan(cr, uid, context=context)

    _defaults = {
        'currency_id': _get_euro,
        'rml_paper_format': 'a4',
        'rml_header': _get_header,
        'rml_header2': _header2,
        'rml_header3': _header3,
        'logo': _get_logo,
        'font': _get_font,
    }

    _constraints = [
        (osv.osv._check_recursion,
         'Error! You can not create recursive companies.', ['parent_id'])
    ]
예제 #30
0
파일: badge.py 프로젝트: LiberTang0/5
class gamification_badge(osv.Model):
    """Badge object that users can send and receive"""

    CAN_GRANT = 1
    NOBODY_CAN_GRANT = 2
    USER_NOT_VIP = 3
    BADGE_REQUIRED = 4
    TOO_MANY = 5

    _name = 'gamification.badge'
    _description = 'Gamification badge'
    _inherit = ['mail.thread']

    def _get_owners_info(self, cr, uid, ids, name, args, context=None):
        """Return:
            the list of unique res.users ids having received this badge
            the total number of time this badge was granted
            the total number of users this badge was granted to
        """
        result = dict((res_id, {'stat_count': 0, 'stat_count_distinct': 0, 'unique_owner_ids': []}) for res_id in ids)

        cr.execute("""
            SELECT badge_id, count(user_id) as stat_count,
                count(distinct(user_id)) as stat_count_distinct,
                array_agg(distinct(user_id)) as unique_owner_ids
            FROM gamification_badge_user
            WHERE badge_id in %s
            GROUP BY badge_id
            """, (tuple(ids),))
        for (badge_id, stat_count, stat_count_distinct, unique_owner_ids) in cr.fetchall():
            result[badge_id] = {
                'stat_count': stat_count,
                'stat_count_distinct': stat_count_distinct,
                'unique_owner_ids': unique_owner_ids,
            }
        return result

    def _get_badge_user_stats(self, cr, uid, ids, name, args, context=None):
        """Return stats related to badge users"""
        result = dict.fromkeys(ids, False)
        badge_user_obj = self.pool.get('gamification.badge.user')
        first_month_day = date.today().replace(day=1).strftime(DF)
        for bid in ids:
            result[bid] = {
                'stat_my': badge_user_obj.search(cr, uid, [('badge_id', '=', bid), ('user_id', '=', uid)], context=context, count=True),
                'stat_this_month': badge_user_obj.search(cr, uid, [('badge_id', '=', bid), ('create_date', '>=', first_month_day)], context=context, count=True),
                'stat_my_this_month': badge_user_obj.search(cr, uid, [('badge_id', '=', bid), ('user_id', '=', uid), ('create_date', '>=', first_month_day)], context=context, count=True),
                'stat_my_monthly_sending': badge_user_obj.search(cr, uid, [('badge_id', '=', bid), ('create_uid', '=', uid), ('create_date', '>=', first_month_day)], context=context, count=True)
            }
        return result

    def _remaining_sending_calc(self, cr, uid, ids, name, args, context=None):
        """Computes the number of badges remaining the user can send

        0 if not allowed or no remaining
        integer if limited sending
        -1 if infinite (should not be displayed)
        """
        result = dict.fromkeys(ids, False)
        for badge in self.browse(cr, uid, ids, context=context):
            if self._can_grant_badge(cr, uid, badge.id, context) != 1:
                # if the user cannot grant this badge at all, result is 0
                result[badge.id] = 0
            elif not badge.rule_max:
                # if there is no limitation, -1 is returned which means 'infinite'
                result[badge.id] = -1
            else:
                result[badge.id] = badge.rule_max_number - badge.stat_my_monthly_sending
        return result

    _columns = {
        'name': fields.char('Badge', required=True, translate=True),
        'description': fields.text('Description', translate=True),
        'image': fields.binary("Image", attachment=True,
            help="This field holds the image used for the badge, limited to 256x256"),
        'rule_auth': fields.selection([
                ('everyone', 'Everyone'),
                ('users', 'A selected list of users'),
                ('having', 'People having some badges'),
                ('nobody', 'No one, assigned through challenges'),
            ],
            string="Allowance to Grant",
            help="Who can grant this badge",
            required=True),
        'rule_auth_user_ids': fields.many2many('res.users', 'rel_badge_auth_users',
            string='Authorized Users',
            help="Only these people can give this badge"),
        'rule_auth_badge_ids': fields.many2many('gamification.badge',
            'gamification_badge_rule_badge_rel', 'badge1_id', 'badge2_id',
            string='Required Badges',
            help="Only the people having these badges can give this badge"),

        'rule_max': fields.boolean('Monthly Limited Sending',
            help="Check to set a monthly limit per person of sending this badge"),
        'rule_max_number': fields.integer('Limitation Number',
            help="The maximum number of time this badge can be sent per month per person."),
        'stat_my_monthly_sending': fields.function(_get_badge_user_stats,
            type="integer",
            string='My Monthly Sending Total',
            multi='badge_users',
            help="The number of time the current user has sent this badge this month."),
        'remaining_sending': fields.function(_remaining_sending_calc, type='integer',
            string='Remaining Sending Allowed', help="If a maxium is set"),

        'challenge_ids': fields.one2many('gamification.challenge', 'reward_id',
            string="Reward of Challenges"),

        'goal_definition_ids': fields.many2many('gamification.goal.definition', 'badge_unlocked_definition_rel',
            string='Rewarded by',
            help="The users that have succeeded theses goals will receive automatically the badge."),

        'owner_ids': fields.one2many('gamification.badge.user', 'badge_id',
            string='Owners', help='The list of instances of this badge granted to users'),
        'active': fields.boolean('Active'),
        'unique_owner_ids': fields.function(_get_owners_info,
            string='Unique Owners',
            help="The list of unique users having received this badge.",
            multi='unique_users',
            type="many2many", relation="res.users"),

        'stat_count': fields.function(_get_owners_info, string='Total',
            type="integer",
            multi='unique_users',
            help="The number of time this badge has been received."),
        'stat_count_distinct': fields.function(_get_owners_info,
            type="integer",
            string='Number of users',
            multi='unique_users',
            help="The number of time this badge has been received by unique users."),
        'stat_this_month': fields.function(_get_badge_user_stats,
            type="integer",
            string='Monthly total',
            multi='badge_users',
            help="The number of time this badge has been received this month."),
        'stat_my': fields.function(_get_badge_user_stats, string='My Total',
            type="integer",
            multi='badge_users',
            help="The number of time the current user has received this badge."),
        'stat_my_this_month': fields.function(_get_badge_user_stats,
            type="integer",
            string='My Monthly Total',
            multi='badge_users',
            help="The number of time the current user has received this badge this month."),
    }

    _defaults = {
        'rule_auth': 'everyone',
        'active': True,
    }

    def check_granting(self, cr, uid, badge_id, context=None):
        """Check the user 'uid' can grant the badge 'badge_id' and raise the appropriate exception
        if not

        Do not check for SUPERUSER_ID
        """
        status_code = self._can_grant_badge(cr, uid, badge_id, context=context)
        if status_code == self.CAN_GRANT:
            return True
        elif status_code == self.NOBODY_CAN_GRANT:
            raise UserError(_('This badge can not be sent by users.'))
        elif status_code == self.USER_NOT_VIP:
            raise UserError(_('You are not in the user allowed list.'))
        elif status_code == self.BADGE_REQUIRED:
            raise UserError(_('You do not have the required badges.'))
        elif status_code == self.TOO_MANY:
            raise UserError(_('You have already sent this badge too many time this month.'))
        else:
            _logger.exception("Unknown badge status code: %d" % int(status_code))
        return False

    def _can_grant_badge(self, cr, uid, badge_id, context=None):
        """Check if a user can grant a badge to another user

        :param uid: the id of the res.users trying to send the badge
        :param badge_id: the granted badge id
        :return: integer representing the permission.
        """
        if uid == SUPERUSER_ID:
            return self.CAN_GRANT

        badge = self.browse(cr, uid, badge_id, context=context)

        if badge.rule_auth == 'nobody':
            return self.NOBODY_CAN_GRANT

        elif badge.rule_auth == 'users' and uid not in [user.id for user in badge.rule_auth_user_ids]:
            return self.USER_NOT_VIP

        elif badge.rule_auth == 'having':
            all_user_badges = self.pool.get('gamification.badge.user').search(cr, uid, [('user_id', '=', uid)], context=context)
            for required_badge in badge.rule_auth_badge_ids:
                if required_badge.id not in all_user_badges:
                    return self.BADGE_REQUIRED

        if badge.rule_max and badge.stat_my_monthly_sending >= badge.rule_max_number:
            return self.TOO_MANY

        # badge.rule_auth == 'everyone' -> no check
        return self.CAN_GRANT

    def check_progress(self, cr, uid, context=None):
        try:
            model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'badge_hidden')
        except ValueError:
            return True
        badge_user_obj = self.pool.get('gamification.badge.user')
        if not badge_user_obj.search(cr, uid, [('user_id', '=', uid), ('badge_id', '=', res_id)], context=context):
            values = {
                'user_id': uid,
                'badge_id': res_id,
            }
            badge_user_obj.create(cr, SUPERUSER_ID, values, context=context)
        return True
예제 #31
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