Example #1
0
class wx_video_message_record(models.Model):
    _inherit = 'wx.message_record'
    _name = 'wx.video_message_record'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'video'))
    message_mediaId = fields.Char("视频消息媒体id")
    message_videodata = fields.Binary(string="视频数据")
    message_thumbMediaId = fields.Char("缩略图id")
    message_thumbMediadata = fields.Binary("缩略图")
    message_msgid = fields.Char("消息ID")
    message_title = fields.Char("视频标题")
    message_description = fields.Char("视频描述")
    #toUserName = fields.Many2one('wx.membership',string="接收方/发送方")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
    qy_toUserName = fields.Many2one('hr.employee', string="企业应用接收方/发送方")
    message_template = fields.Many2one('wx.video_message_template',
                                       string="视频消息模板")
    association_order = fields.Char('关联实体编码或单号')
Example #2
0
class plm_document_relation(models.Model):
    _name           =   'plm.document.relation'
    _inherit        =   'plm.document.relation'
    
    parent_preview  =   fields.Binary   (related="parent_id.preview",       string=_("Preview"),    store=False)
    parent_state    =   fields.Selection(related="parent_id.state",         string=_("Status"),     store=False)
    parent_revision =   fields.Integer  (related="parent_id.revisionid",    string=_("Revision"),   store=False)
    child_preview   =   fields.Binary   (related="child_id.preview",        string=_("Preview"),    store=False)
    child_state     =   fields.Selection(related="child_id.state",          string=_("Status"),     store=False)
    child_revision  =   fields.Integer  (related="child_id.revisionid",     string=_("Revision"),   store=False)
Example #3
0
class wx_music_message_record(models.Model):
    _inherit = 'wx.message_record'
    _name = 'wx.music_message_record'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'music'))
    message_title = fields.Char("音乐标题")
    message_description = fields.Char("音乐描述")
    message_musicURL = fields.Char("音乐链接")
    message_HQMusicUrl = fields.Char("高质量音乐链接")
    message_ThumbMediaId = fields.Char("缩略图的媒体id")
    message_ThumbMediaData = fields.Binary("缩略图的媒体数据")
    #toUserName = fields.Many2one('wx.membership',string="接收方/发送方")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
    qy_toUserName = fields.Many2one('hr.employee', string="企业应用接收方/发送方")
    message_template = fields.Many2one('wx.music_message_template',
                                       string="音乐消息模板")
    association_order = fields.Char('关联实体编码或单号')
Example #4
0
class wx_voice_message_record(models.Model):
    _inherit = 'wx.message_record'
    _name = 'wx.voice_message_record'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'voice'))
    message_format = fields.Char("语音格式")
    message_mediaId = fields.Char("语音消息媒体id")
    message_voicedata = fields.Binary(string="语音数据")
    message_recognition = fields.Char("语音识别结果")
    message_msgid = fields.Char("消息ID")
    #toUserName = fields.Many2one('wx.membership',string="接收方/发送方")
    qy_toUserName = fields.Many2one('hr.employee', string="企业应用接收方/发送方")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
    message_template = fields.Many2one('wx.voice_message_template',
                                       string="语音消息模板")
    association_order = fields.Char('关联实体编码或单号')
Example #5
0
class customized_rfq(models.Model):
    _inherit = ["purchase.order"]

    @api.model
    def _default_rfq_template(self):
        company_obj = self.env['res.company']
        company = self.env['res.users'].browse([self.env.user.id]).company_id
        if not company.template_rfq:
            def_tpl = self.env['ir.ui.view'].search(
                [('key', 'like', 'professional_templates.RFQ_%'),
                 ('type', '=', 'qweb')],
                order='id asc',
                limit=1)
            company.write({'template_rfq': def_tpl.id})
        return company.template_rfq or self.env.ref(
            'purchase.report_purchasequotation_document')

    rfq_logo = fields.Binary(
        "Logo",
        attachment=True,
        help=
        "This field holds the image used as logo for the RFQ, if non is uploaded, the default logo define in the company settings will be used"
    )
    templ_rfq_id = fields.Many2one(
        'ir.ui.view',
        'RFQ Template',
        default=_default_rfq_template,
        required=True,
        domain=
        "[('type', '=', 'qweb'), ('key', 'like', 'professional_templates.RFQ\_%\_document' )]"
    )
Example #6
0
class OpStudent(models.Model):
    _name = 'op.student'
    _inherits = {'res.partner': 'partner_id'}

    @api.one
    @api.depends('roll_number_line', 'batch_id', 'course_id')
    def _get_curr_roll_number(self):
        # TO_DO:: Improve the logic by adding sequence field in course.
        if self.roll_number_line:
            for roll_no in self.roll_number_line:
                if roll_no.course_id == self.course_id and \
                        roll_no.batch_id == self.batch_id:
                    self.roll_number = roll_no.roll_number
        else:
            self.roll_number = 0

    middle_name = fields.Char('Middle Name', size=128)
    last_name = fields.Char('Last Name', size=128, required=True)
    birth_date = fields.Date('Birth Date', required=True)
    blood_group = fields.Selection([('A+', 'A+ve'), ('B+', 'B+ve'),
                                    ('O+', 'O+ve'), ('AB+', 'AB+ve'),
                                    ('A-', 'A-ve'), ('B-', 'B-ve'),
                                    ('O-', 'O-ve'), ('AB-', 'AB-ve')],
                                   'Blood Group')
    gender = fields.Selection([('m', 'Male'), ('f', 'Female'), ('o', 'Other')],
                              'Gender',
                              required=True)
    nationality = fields.Many2one('res.country', 'Nationality')
    emergency_contact = fields.Many2one('res.partner', 'Emergency Contact')
    visa_info = fields.Char('Visa Info', size=64)
    id_number = fields.Char('ID Card Number', size=64)
    photo = fields.Binary('Photo')
    course_id = fields.Many2one('op.course', 'Course', required=True)
    batch_id = fields.Many2one('op.batch', 'Batch', required=True)
    roll_number_line = fields.One2many('op.roll.number', 'student_id',
                                       'Roll Number')
    partner_id = fields.Many2one('res.partner',
                                 'Partner',
                                 required=True,
                                 ondelete="cascade")
    roll_number = fields.Char('Current Roll Number',
                              compute='_get_curr_roll_number',
                              size=8,
                              store=True)
    gr_no = fields.Char("GR Number", size=20)

    @api.one
    @api.constrains('birth_date')
    def _check_birthdate(self):
        if self.birth_date > fields.Date.today():
            raise ValidationError(
                "Birth Date can't be greater than current date!")

    @api.onchange('course_id')
    def onchange_course(self):
        self.batch_id = False
Example #7
0
class wx_video_message_template(models.Model):
    _inherit = 'wx.message_template_base'
    _name = 'wx.video_message_template'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'video'))
    message_videodata = fields.Binary(string="视频数据")
    message_videodata_url = fields.Char(string="视频数据地址", readonly=True)
    message_thumbMediadata = fields.Binary("缩略图")
    message_thumbMedia_url = fields.Char("缩略图地址", readonly=True)
    message_title = fields.Char("视频标题")
    message_description = fields.Char("视频描述")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
Example #8
0
class OpFaculty(models.Model):
    _name = 'op.faculty'
    _inherits = {'res.partner': 'partner_id'}

    partner_id = fields.Many2one('res.partner',
                                 'Partner',
                                 required=True,
                                 ondelete="cascade")
    middle_name = fields.Char('Middle Name', size=128)
    last_name = fields.Char('Last Name', size=128, required=True)
    birth_date = fields.Date('Birth Date', required=True)
    blood_group = fields.Selection([('A+', 'A+ve'), ('B+', 'B+ve'),
                                    ('O+', 'O+ve'), ('AB+', 'AB+ve'),
                                    ('A-', 'A-ve'), ('B-', 'B-ve'),
                                    ('O-', 'O-ve'), ('AB-', 'AB-ve')],
                                   'Blood Group')
    gender = fields.Selection([('male', 'Male'), ('female', 'Female')],
                              'Gender',
                              required=True)
    nationality = fields.Many2one('res.country', 'Nationality')
    emergency_contact = fields.Many2one('res.partner', 'Emergency Contact')
    visa_info = fields.Char('Visa Info', size=64)
    id_number = fields.Char('ID Card Number', size=64)
    photo = fields.Binary('Photo')
    login = fields.Char('Login',
                        related='partner_id.user_id.login',
                        readonly=1)
    last_login = fields.Datetime('Latest Connection',
                                 related='partner_id.user_id.login_date',
                                 readonly=1)
    faculty_subject_ids = fields.Many2many('op.subject', string='Subject(s)')
    emp_id = fields.Many2one('hr.employee', 'Employee')

    @api.one
    @api.constrains('birth_date')
    def _check_birthdate(self):
        if self.birth_date > fields.Date.today():
            raise ValidationError(
                "Birth Date can't be greater than current date!")

    @api.one
    def create_employee(self):
        vals = {
            'name':
            self.name + ' ' + (self.middle_name or '') + ' ' + self.last_name,
            'country_id': self.nationality.id,
            'gender': self.gender,
            'address_home_id': self.partner_id.id
        }
        emp_id = self.env['hr.employee'].create(vals)
        self.write({'emp_id': emp_id.id})
        self.partner_id.write({'supplier': True, 'employee': True})
Example #9
0
class pos_cache(models.Model):
    _name = 'pos.cache'

    cache = fields.Binary()
    product_domain = fields.Text(required=True)
    product_fields = fields.Text(required=True)

    config_id = fields.Many2one('pos.config',
                                ondelete='cascade',
                                required=True)
    compute_user_id = fields.Many2one('res.users',
                                      'Cache compute user',
                                      required=True)

    @api.model
    def refresh_all_caches(self):
        self.env['pos.cache'].search([]).refresh_cache()

    @api.one
    def refresh_cache(self):
        products = self.env['product.product'].search(
            self.get_product_domain())
        prod_ctx = products.with_context(
            pricelist=self.config_id.pricelist_id.id,
            display_default_code=False)
        prod_ctx = prod_ctx.sudo(self.compute_user_id.id)
        res = prod_ctx.read(self.get_product_fields())
        datas = {
            'cache': cPickle.dumps(res, protocol=cPickle.HIGHEST_PROTOCOL),
        }

        self.write(datas)

    @api.model
    def get_product_domain(self):
        return literal_eval(self.product_domain)

    @api.model
    def get_product_fields(self):
        return literal_eval(self.product_fields)

    @api.model
    def get_cache(self, domain, fields):
        if not self.cache or domain != self.get_product_domain(
        ) or fields != self.get_product_fields():
            self.product_domain = str(domain)
            self.product_fields = str(fields)
            self.refresh_cache()

        return cPickle.loads(self.cache)
Example #10
0
class news(models.Model):
    _name = 'wx.message_mpnews_template'
    news_id = fields.Many2one('wx.mpnews_message_template')
    message_title = fields.Text(string="图文消息标题")
    message_description = fields.Char(string="图文消息描述")
    message_picurl = fields.Char(string="图片链接")  # 较好的效果为大图360*200,小图200*200
    message_url = fields.Char(string="消息跳转链接")
    message_imagedata = fields.Binary(string="图片信息")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
Example #11
0
class wx_product_image(models.Model):
    _name = 'wx.product.images'
    wx_product_id = fields.Many2one('wx.product', '微信商品')
    image_uses = fields.Selection([('main_image', '主图'),
                                   ('other_image', '其它图片'),
                                   ('detail_image', '详情图片')],
                                  string='图片用途',
                                  default='detail_image')
    serial_number = fields.Integer(string='序号')
    image = fields.Binary("图片")
    text = fields.Text(string="文字")
    image_url = fields.Char(string='图片地址')
    sync_upload = fields.Boolean(string='同步时需上传', help='同步商品时有效')
    _order = 'serial_number'
Example #12
0
class customized_picking_slip(models.Model):
	_inherit=["stock.picking"]

	@api.model
	def _default_picking_template(self):
	    company_obj = self.env['res.company']
	    company = self.env['res.users'].browse([self.env.user.id]).company_id
	    if not company.template_pk:
		def_tpl = self.env['ir.ui.view'].search([('key', 'like', 'professional_templates.PICK_%' ), ('type', '=', 'qweb')], order='id asc', limit=1)
                company.write({'template_pk': def_tpl.id})
	    return company.template_pk or self.env.ref('stock.report_picking')
	
 	pk_logo = fields.Binary("Logo", attachment=True,
             help="This field holds the image used as logo for the Picking, if non is uploaded, the default logo set in the company settings will be used")
	templ_pk_id = fields.Many2one('ir.ui.view', 'Picking Slip Template', default=_default_picking_template,required=True, 
		domain="[('type', '=', 'qweb'), ('key', 'like', 'professional_templates.PICK\_%\_document' )]")
Example #13
0
class wx_voice_message_template(models.Model):
    _inherit = 'wx.message_template_base'
    _name = 'wx.voice_message_template'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'voice'))
    message_voicedata = fields.Binary(string="语音数据")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
Example #14
0
class wx_image_message_record(models.Model):
    _inherit = 'wx.message_record'
    _name = 'wx.image_message_record'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'image'))
    message_picurl = fields.Char("图片链接地址")
    message_mediaId = fields.Char("图片消息媒体id")
    message_imagedata = fields.Binary(string="图片信息")
    message_msgid = fields.Char("消息ID")
    message_template = fields.Many2one('wx.image_message_template',
                                       string="图片消息模板")
    #toUserName = fields.Many2one('wx.membership',string="接收方/发送方")
    qy_toUserName = fields.Many2one('hr.employee', string="企业应用接收方/发送方")
    association_order = fields.Char('关联实体编码或单号')
Example #15
0
class event_sponsors(models.Model):
    _name = "event.sponsor"
    _order = "sequence"

    event_id = fields.Many2one('event.event', 'Event', required=True)
    sponsor_type_id = fields.Many2one('event.sponsor.type',
                                      'Sponsoring Type',
                                      required=True)
    partner_id = fields.Many2one('res.partner',
                                 'Sponsor/Customer',
                                 required=True)
    url = fields.Char('Sponsor Website')
    sequence = fields.Integer('Sequence',
                              store=True,
                              related='sponsor_type_id.sequence')
    image_medium = fields.Binary(string='Logo',
                                 related='partner_id.image_medium',
                                 store=True,
                                 attachment=True)
Example #16
0
class wx_music_message_template(models.Model):
    _inherit = 'wx.message_template_base'
    _name = 'wx.music_message_template'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        readonly=True,
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'music'))
    message_title = fields.Char("音乐标题")
    message_description = fields.Char("音乐描述")
    message_musicURL = fields.Char("音乐链接")
    message_HQMusicUrl = fields.Char("高质量音乐链接")
    message_ThumbMediaData = fields.Binary("缩略图的媒体数据")
    message_ThumbMediaId = fields.Char("缩略图的媒体id")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
Example #17
0
class wx_mpnews_message_template(models.Model):
    _inherit = 'wx.message_template_base'
    _name = 'wx.mpnews_message_template'
    message_type = fields.Many2one(
        'wx.messagetype',
        string='消息类型',
        default=lambda self: _messagetype_get(self, self.env.cr, self.env.user.
                                              id, 'imagetext'))
    message_news = fields.One2many('wx.message_mpnews_template',
                                   'news_id',
                                   string="图文消息")
    message_title = fields.Text(string="图文消息标题")
    message_description = fields.Char(string="图文消息描述")
    message_picurl = fields.Char(string="图片链接")  # 较好的效果为大图360*200,小图200*200
    message_url = fields.Char(string="消息跳转链接")
    message_imagedata = fields.Binary(string="图片信息")
    model_id = fields.Many2one(
        'ir.model',
        '应用模型',
        ondelete='cascade',
        copy=False,
        help="Base model on which the server action runs.")
Example #18
0
class wx_customer(models.Model):
    _name = 'wx.customer'
    _rec_name = 'nickname'
    openid = fields.Char(string='微信ID', readonly=True)
    unionid = fields.Char(string='UNIONID', readonly=True)
    nickname = fields.Char(string='昵称', readonly=True)
    sex = fields.Selection([('male', '男'), ('female', '女'), ('other', '保密')],
                           string='性别',
                           readonly=True)
    country = fields.Many2one('res.country', string='国家', readonly=True)
    province = fields.Many2one('res.country.state',
                               string='省/直辖市',
                               readonly=True)
    city = fields.Char(string='城市', readonly=True)
    headimg = fields.Binary(string='头像', readonly=True)
    officialaccount_id = fields.Many2one('wx.officialaccount',
                                         string='服务号',
                                         readonly=True)
    # customer=fields.Many2one('')
    subscribe_infos = fields.One2many('wx.customer.subscribe',
                                      'wx_customer_id',
                                      string='关注信息')

    _sql_constraints = [('openid_uniq', 'unique(openid)', '微信ID必须唯一!')]
Example #19
0
class customized_po_order(models.Model):
    _inherit = ["purchase.order"]

    @api.model
    def _default_template(self):
        company_obj = self.env['res.company']
        company = self.env['res.users'].browse([self.env.user.id]).company_id
        if not company.template_po:
            def_tpl = self.env['ir.ui.view'].search(
                [('key', 'like', 'professional_templates.PO_%'),
                 ('type', '=', 'qweb')],
                order='id asc',
                limit=1)
            company.write({'template_po': def_tpl.id})
        return company.template_po or self.env.ref(
            'purchase.report_purchaseorder_document')

    @api.model
    def _default_odd(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).odd

    @api.model
    def _default_even(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).even

    @api.model
    def _default_theme_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).theme_color

    @api.model
    def _default_theme_txt_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).theme_txt_color

    @api.model
    def _default_name_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).name_color

    @api.model
    def _default_cust_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).cust_color

    @api.model
    def _default_text_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).text_color

    po_logo = fields.Binary(
        "Logo",
        attachment=True,
        help=
        "This field holds the image used as logo for the purchase order, if non is uploaded, the default logo set in the company settings will be used"
    )
    templ_id = fields.Many2one(
        'ir.ui.view',
        'Purchase Order Template',
        default=_default_template,
        required=True,
        domain=
        "[('type', '=', 'qweb'), ('key', 'like', 'professional_templates.PO\_%\_document' )]"
    )
    odd = fields.Char(
        'Odd parity Color',
        size=7,
        required=True,
        default=_default_odd,
        help="The background color for odd lines in the purchase order")
    even = fields.Char(
        'Even parity Color',
        size=7,
        required=True,
        default=_default_even,
        help="The background color for even lines in the purchase order")
    theme_color = fields.Char(
        'Theme Color',
        size=7,
        required=True,
        default=_default_theme_color,
        help=
        "The Main Theme color of the purchase order/quaotation. Normally this should be one of your official company colors"
    )
    theme_txt_color = fields.Char(
        'Theme Text Color',
        size=7,
        required=True,
        default=_default_theme_txt_color,
        help=
        "The Text color of the areas with theme color. This should not be the same the theme color"
    )
    text_color = fields.Char('Text Color',
                             size=7,
                             required=True,
                             default=_default_text_color,
                             help="The text color of the order. Normally this\
			 should be one of your official company colors or default HTML text color")
    name_color = fields.Char('Company Name Color',
                             size=7,
                             required=True,
                             default=_default_name_color,
                             help="The Text color of the Company Name. \
			Normally thisshould be one of your official company colors or default HTML text color"
                             )
    cust_color = fields.Char('Customer Name Color',
                             size=7,
                             required=True,
                             default=_default_name_color,
                             help="The Text color of the Customer Name. \
			Normally this should be one of your official company colors or default HTML text color"
                             )
Example #20
0
class plm_spareChoseLanguage(osv.osv.osv_memory):
    _name = "plm.sparechoselanguage"
    _description = "Module for extending the functionality of printing spare_bom reports in a multi language environment"

    @api.v8
    def getInstalledLanguage(self):
        """
            get installed language
        """
        out = []
        modobj = self.env['res.lang']
        for objBrowse in modobj.search([]):
            out.append((objBrowse.code, objBrowse.name))
        return out

    @api.multi
    def print_report(self):
        self.ensure_one()
        lang = self.lang
        if lang:
            modobj = self.env['ir.module.module']
            mids = modobj.search([('state', '=', 'installed')])
            if not mids:
                raise UserError("Language not Installed")
            reportName = 'product.product.spare.parts.pdf'
            if self.onelevel:
                reportName = 'product.product.spare.parts.pdf.one'
            srv = yuancloud.report.interface.report_int._reports['report.' +
                                                                 reportName]
            productProductId = self.env.context.get('active_id')
            newContext = self.env.context.copy()
            newContext['lang'] = lang
            stream, fileExtention = srv.create(self.env.cr,
                                               self.env.uid, [
                                                   productProductId,
                                               ],
                                               {'raise_report_warning': False},
                                               context=newContext)
            self.datas = base64.encodestring(stream)
            tProductProduct = self.env['product.product']
            brwProduct = tProductProduct.browse(productProductId)
            fileName = brwProduct.name + "_" + lang + "_manual." + fileExtention
            self.datas_name = fileName
            return {
                'context': self.env.context,
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': plm_spareChoseLanguage._name,
                'res_id': self.id,
                'view_id': False,
                'type': 'ir.actions.act_window',
                'target': 'new',
            }
        UserError(_("Select a language"))

    lang = fields.Selection(getInstalledLanguage, 'Language', required=True)

    onelevel = fields.Boolean(
        'One Level',
        help="If you check this box, the report will be made in one level")

    datas = fields.Binary("Download", readonly=True)

    datas_name = fields.Char('Download file name ', size=255, readonly=True)

    _defaults = {'onelevel': False}
Example #21
0
class plm_bomChoseLanguage(osv.osv.osv_memory):
    _name = "plm.bomchoselanguage"
    _description = "Module for extending the functionality of printing bom reports in a multi language environment"

    @api.v8
    def getInstalledLanguage(self):
        """
            get installed language
        """
        out = []
        modobj = self.env['res.lang']
        for objBrowse in modobj.search([]):
            out.append((objBrowse.code, objBrowse.name))
        return out

    @api.multi
    def print_report(self):
        self.ensure_one()
        lang = self.lang
        if lang:
            modobj = self.env['ir.module.module']
            mids = modobj.search([('state', '=', 'installed')])
            if not mids:
                raise UserError("Language not Installed")
            reportName = self.bom_type
            srv = yuancloud.report.interface.report_int._reports['report.' +
                                                                 reportName]
            bomId = self.env.context.get('active_id')
            newContext = self.env.context.copy()
            newContext['lang'] = lang
            stream, fileExtention = srv.create(self.env.cr,
                                               self.env.uid, [
                                                   bomId,
                                               ],
                                               {'raise_report_warning': False},
                                               context=newContext)
            self.datas = base64.encodestring(stream)
            tMrpBom = self.env['mrp.bom']
            brwProduct = tMrpBom.browse(bomId)
            fileName = brwProduct.product_id.name + "_" + lang + "_bom." + fileExtention
            self.datas_name = fileName
            return {
                'context': self.env.context,
                'view_type': 'form',
                'view_mode': 'form',
                'res_model': plm_bomChoseLanguage._name,
                'res_id': self.id,
                'view_id': False,
                'type': 'ir.actions.act_window',
                'target': 'new',
            }
        UserError(_("Select a language"))

    lang = fields.Selection(getInstalledLanguage, _('Language'), required=True)

    bom_type = fields.Selection(
        AVAILABLE_REPORT,
        _('Bom Report Type'),
        required=True,
        help=_("Chose the Bom report you would like to print"))

    datas = fields.Binary(_("Download"), readonly=True)

    datas_name = fields.Char(_('Download file name '), size=255, readonly=True)

    _defaults = {'bom_type': False}
Example #22
0
class Slide(models.Model):
    """ This model represents actual presentations. Those must be one of four
    types:

     - Presentation
     - Document
     - Infographic
     - Video

    Slide has various statistics like view count, embed count, like, dislikes """

    _name = 'slide.slide'
    _inherit = [
        'mail.thread', 'website.seo.metadata', 'website.published.mixin'
    ]
    _description = 'Slides'

    _PROMOTIONAL_FIELDS = [
        '__last_update', 'name', 'image_thumb', 'image_medium', 'slide_type',
        'total_views', 'category_id', 'channel_id', 'description', 'tag_ids',
        'write_date', 'create_date', 'website_published', 'website_url',
        'website_meta_title', 'website_meta_description',
        'website_meta_keywords'
    ]

    _sql_constraints = [('name_uniq', 'UNIQUE(channel_id, name)',
                         'The slide name must be unique within a channel')]

    # description
    name = fields.Char('Title', required=True, translate=True)
    description = fields.Text('Description', translate=True)
    channel_id = fields.Many2one('slide.channel',
                                 string="Channel",
                                 required=True)
    category_id = fields.Many2one('slide.category',
                                  string="Category",
                                  domain="[('channel_id', '=', channel_id)]")
    tag_ids = fields.Many2many('slide.tag',
                               'rel_slide_tag',
                               'slide_id',
                               'tag_id',
                               string='Tags')
    download_security = fields.Selection([('none', 'No One'),
                                          ('user', 'Authentified Users Only'),
                                          ('public', 'Everyone')],
                                         string='Download Security',
                                         required=True,
                                         default='user')
    image = fields.Binary('Image', attachment=True)
    image_medium = fields.Binary('Medium',
                                 compute="_get_image",
                                 store=True,
                                 attachment=True)
    image_thumb = fields.Binary('Thumbnail',
                                compute="_get_image",
                                store=True,
                                attachment=True)

    @api.depends('image')
    def _get_image(self):
        for record in self:
            if record.image:
                record.image_medium = image.crop_image(record.image,
                                                       type='top',
                                                       ratio=(4, 3),
                                                       thumbnail_ratio=4)
                record.image_thumb = image.crop_image(record.image,
                                                      type='top',
                                                      ratio=(4, 3),
                                                      thumbnail_ratio=6)
            else:
                record.image_medium = False
                record.iamge_thumb = False

    # content
    slide_type = fields.Selection(
        [('infographic', 'Infographic'), ('presentation', 'Presentation'),
         ('document', 'Document'), ('video', 'Video')],
        string='Type',
        required=True,
        default='document',
        help=
        "Document type will be set automatically depending on file type, height and width."
    )
    index_content = fields.Text('Transcript')
    datas = fields.Binary('Content')
    url = fields.Char('Document URL', help="Youtube or Google Document URL")
    document_id = fields.Char('Document ID',
                              help="Youtube or Google Document ID")
    mime_type = fields.Char('Mime-type')

    @api.onchange('url')
    def on_change_url(self):
        self.ensure_one()
        if self.url:
            res = self._parse_document_url(self.url)
            if res.get('error'):
                raise Warning(
                    _('Could not fetch data from url. Document or access right not available:\n%s'
                      ) % res['error'])
            values = res['values']
            if not values.get('document_id'):
                raise Warning(
                    _('Please enter valid Youtube or Google Doc URL'))
            for key, value in values.iteritems():
                setattr(self, key, value)

    # website
    date_published = fields.Datetime('Publish Date')
    website_message_ids = fields.One2many(
        'mail.message',
        'res_id',
        domain=lambda self: [('model', '=', self._name),
                             ('message_type', '=', 'comment')],
        string='Website Messages',
        help="Website communication history")
    likes = fields.Integer('Likes')
    dislikes = fields.Integer('Dislikes')
    # views
    embedcount_ids = fields.One2many('slide.embed',
                                     'slide_id',
                                     string="Embed Count")
    slide_views = fields.Integer('# of Website Views')
    embed_views = fields.Integer('# of Embedded Views')
    total_views = fields.Integer("Total # Views",
                                 default="0",
                                 compute='_compute_total',
                                 store=True)

    @api.depends('slide_views', 'embed_views')
    def _compute_total(self):
        for record in self:
            record.total_views = record.slide_views + record.embed_views

    embed_code = fields.Text('Embed Code',
                             readonly=True,
                             compute='_get_embed_code')

    def _get_embed_code(self):
        base_url = self.env['ir.config_parameter'].get_param('web.base.url')
        for record in self:
            if record.datas and not record.document_id:
                record.embed_code = '<iframe src="%s/slides/embed/%s?page=1" allowFullScreen="true" height="%s" width="%s" frameborder="0"></iframe>' % (
                    base_url, record.id, 315, 420)
            elif record.slide_type == 'video' and record.document_id:
                if not record.mime_type:
                    # embed youtube video
                    record.embed_code = '<iframe src="//www.youtube.com/embed/%s?theme=light" allowFullScreen="true" frameborder="0"></iframe>' % (
                        record.document_id)
                else:
                    # embed google doc video
                    record.embed_code = '<embed src="https://video.google.com/get_player?ps=docs&partnerid=30&docid=%s" type="application/x-shockwave-flash"></embed>' % (
                        record.document_id)
            else:
                record.embed_code = False

    @api.multi
    @api.depends('name')
    def _website_url(self, name, arg):
        res = super(Slide, self)._website_url(name, arg)
        base_url = self.env['ir.config_parameter'].get_param('web.base.url')
        #link_tracker is not in dependencies, so use it to shorten url only if installed.
        if self.env.registry.get('link.tracker'):
            LinkTracker = self.env['link.tracker']
            res.update({(slide.id, LinkTracker.sudo().create({
                'url':
                '%s/slides/slide/%s' % (base_url, slug(slide))
            }).short_url)
                        for slide in self})
        else:
            res.update({(slide.id,
                         '%s/slides/slide/%s' % (base_url, slug(slide)))
                        for slide in self})
        return res

    @api.model
    def create(self, values):
        if not values.get('index_content'):
            values['index_content'] = values.get('description')
        if values.get(
                'slide_type') == 'infographic' and not values.get('image'):
            values['image'] = values['datas']
        if values.get(
                'website_published') and not values.get('date_published'):
            values['date_published'] = datetime.datetime.now()
        if values.get('url'):
            doc_data = self._parse_document_url(values['url']).get(
                'values', dict())
            for key, value in doc_data.iteritems():
                values.setdefault(key, value)
        # Do not publish slide if user has not publisher rights
        if not self.user_has_groups('base.group_website_publisher'):
            values['website_published'] = False
        slide = super(Slide, self).create(values)
        slide.channel_id.message_subscribe_users()
        slide._post_publication()
        return slide

    @api.multi
    def write(self, values):
        if values.get('url'):
            doc_data = self._parse_document_url(values['url']).get(
                'values', dict())
            for key, value in doc_data.iteritems():
                values.setdefault(key, value)
        res = super(Slide, self).write(values)
        if values.get('website_published'):
            self.date_published = datetime.datetime.now()
            self._post_publication()
        return res

    @api.model
    def check_field_access_rights(self, operation, fields):
        """ As per channel access configuration (visibility)
         - public  ==> no restriction on slides access
         - private ==> restrict all slides of channel based on access group defined on channel group_ids field
         - partial ==> show channel, but presentations based on groups means any user can see channel but not slide's content.
        For private: implement using record rule
        For partial: user can see channel, but channel gridview have slide detail so we have to implement
        partial field access mechanism for public user so he can have access of promotional field (name, view_count) of slides,
        but not all fields like data (actual pdf content)
        all fields should be accessible only for user group defined on channel group_ids
        """
        if self.env.uid == SUPERUSER_ID:
            return fields or list(self._fields)
        fields = super(Slide,
                       self).check_field_access_rights(operation, fields)
        # still read not perform so we can not access self.channel_id
        if self.ids:
            self.env.cr.execute(
                'SELECT DISTINCT channel_id FROM ' + self._table +
                ' WHERE id IN %s', (tuple(self.ids), ))
            channel_ids = [x[0] for x in self.env.cr.fetchall()]
            channels = self.env['slide.channel'].sudo().browse(channel_ids)
            limited_access = all(
                channel.visibility == 'partial'
                and not len(channel.group_ids & self.env.user.groups_id)
                for channel in channels)
            if limited_access:
                fields = [
                    field for field in fields
                    if field in self._PROMOTIONAL_FIELDS
                ]
        return fields

    def get_related_slides(self, limit=20):
        domain = [('website_published', '=', True),
                  ('channel_id.visibility', '!=', 'private'),
                  ('id', '!=', self.id)]
        if self.category_id:
            domain += [('category_id', '=', self.category_id.id)]
        for record in self.search(domain, limit=limit):
            yield record

    def get_most_viewed_slides(self, limit=20):
        for record in self.search([('website_published', '=', True),
                                   ('channel_id.visibility', '!=', 'private'),
                                   ('id', '!=', self.id)],
                                  limit=limit,
                                  order='total_views desc'):
            yield record

    def _post_publication(self):
        base_url = self.env['ir.config_parameter'].get_param('web.base.url')
        for slide in self.filtered(lambda slide: slide.website_published):
            publish_template = slide.channel_id.publish_template_id
            html_body = publish_template.with_context({
                'base_url': base_url
            }).render_template(publish_template.body_html, 'slide.slide',
                               slide.id)
            slide.channel_id.message_post(
                body=html_body,
                subtype='website_slides.mt_channel_slide_published')
        return True

    @api.one
    def send_share_email(self, email):
        base_url = self.env['ir.config_parameter'].get_param('web.base.url')
        return self.channel_id.share_template_id.with_context({
            'email':
            email,
            'base_url':
            base_url
        }).send_mail(self.id)

    # --------------------------------------------------
    # Parsing methods
    # --------------------------------------------------

    @api.model
    def _fetch_data(self, base_url, data, content_type=False):
        result = {'values': dict()}
        try:
            if data:
                base_url = base_url + '?%s' % urlencode(data)
            req = urllib2.Request(base_url)
            content = urllib2.urlopen(req).read()
            if content_type == 'json':
                result['values'] = json.loads(content)
            elif content_type in ('image', 'pdf'):
                result['values'] = content.encode('base64')
            else:
                result['values'] = content
        except urllib2.HTTPError as e:
            result['error'] = e.read()
            e.close()
        except urllib2.URLError as e:
            result['error'] = e.reason
        return result

    def _find_document_data_from_url(self, url):
        expr = re.compile(
            r'^.*((youtu.be/)|(v/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*'
        )
        arg = expr.match(url)
        document_id = arg and arg.group(7) or False
        if document_id:
            return ('youtube', document_id)

        expr = re.compile(
            r'(^https:\/\/docs.google.com|^https:\/\/drive.google.com).*\/d\/([^\/]*)'
        )
        arg = expr.match(url)
        document_id = arg and arg.group(2) or False
        if document_id:
            return ('google', document_id)

        return (None, False)

    def _parse_document_url(self, url, only_preview_fields=False):
        document_source, document_id = self._find_document_data_from_url(url)
        if document_source and hasattr(self,
                                       '_parse_%s_document' % document_source):
            return getattr(self, '_parse_%s_document' % document_source)(
                document_id, only_preview_fields)
        return {'error': _('Unknown document')}

    def _parse_youtube_document(self, document_id, only_preview_fields):
        key = self.env['ir.config_parameter'].sudo().get_param(
            'website_slides.google_app_key')
        fetch_res = self._fetch_data(
            'https://www.googleapis.com/youtube/v3/videos', {
                'id': document_id,
                'key': key,
                'part': 'snippet',
                'fields': 'items(id,snippet)'
            }, 'json')
        if fetch_res.get('error'):
            return fetch_res

        values = {'slide_type': 'video', 'document_id': document_id}
        youtube_values = fetch_res['values'].get('items', list(dict()))[0]
        if youtube_values.get('snippet'):
            snippet = youtube_values['snippet']
            if only_preview_fields:
                values.update({
                    'url_src': snippet['thumbnails']['high']['url'],
                    'title': snippet['title'],
                    'description': snippet['description']
                })
                return values
            values.update({
                'name':
                snippet['title'],
                'image':
                self._fetch_data(snippet['thumbnails']['high']['url'], {},
                                 'image')['values'],
                'description':
                snippet['description'],
            })
        return {'values': values}

    @api.model
    def _parse_google_document(self, document_id, only_preview_fields):
        def get_slide_type(vals):
            # TDE FIXME: WTF ??
            image = Image.open(io.BytesIO(vals['image'].decode('base64')))
            width, height = image.size
            if height > width:
                return 'document'
            else:
                return 'presentation'

        key = self.env['ir.config_parameter'].sudo().get_param(
            'website_slides.google_app_key')
        fetch_res = self._fetch_data(
            'https://www.googleapis.com/drive/v2/files/%s' % document_id, {
                'projection': 'BASIC',
                'key': key
            }, "json")
        if fetch_res.get('error'):
            return fetch_res

        google_values = fetch_res['values']
        if only_preview_fields:
            return {
                'url_src': google_values['thumbnailLink'],
                'title': google_values['title'],
            }

        values = {
            'name':
            google_values['title'],
            'image':
            self._fetch_data(
                google_values['thumbnailLink'].replace('=s220', ''), {},
                'image')['values'],
            'mime_type':
            google_values['mimeType'],
            'document_id':
            document_id,
        }
        if google_values['mimeType'].startswith('video/'):
            values['slide_type'] = 'video'
        elif google_values['mimeType'].startswith('image/'):
            values['datas'] = values['image']
            values['slide_type'] = 'infographic'
        elif google_values['mimeType'].startswith(
                'application/vnd.google-apps'):
            values['datas'] = self._fetch_data(
                google_values['exportLinks']['application/pdf'], {},
                'pdf')['values']
            values['slide_type'] = get_slide_type(values)
            if google_values['exportLinks'].get('text/plain'):
                values['index_content'] = self._fetch_data(
                    google_values['exportLinks']['text/plain'], {})['values']
            if google_values['exportLinks'].get('text/csv'):
                values['index_content'] = self._fetch_data(
                    google_values['exportLinks']['text/csv'], {})['values']
        elif google_values['mimeType'] == 'application/pdf':
            # TODO: Google Drive PDF document doesn't provide plain text transcript
            values['datas'] = self._fetch_data(google_values['webContentLink'],
                                               {}, 'pdf')['values']
            values['slide_type'] = get_slide_type(values)

        return {'values': values}
class PrototypeModuleExport(models.TransientModel):
    _name = "module_prototyper.module.export"

    name = fields.Char('File Name', readonly=True)
    api_version = fields.Selection([
        ('5.0', '5.0'),
    ],
                                   'API version',
                                   required=True,
                                   default='5.0')
    data = fields.Binary('File', readonly=True)
    state = fields.Selection(
        [
            ('choose', 'choose'),  # choose version
            ('get', 'get')  # get module
        ],
        default='choose')

    @api.model
    def action_export(self, ids):
        """
        Export a zip file containing the module based on the information
        provided in the prototype, using the templates chosen in the wizard.
        """
        if isinstance(ids, (int, long)):
            ids = [ids]
        wizard = self.browse(ids)

        active_model = self._context.get('active_model')

        # checking if the wizard was called by a prototype.
        msg = '{} has to be called from a "module_prototyper" , not a "{}"'
        assert active_model == 'module_prototyper', msg.format(
            self, active_model)

        # getting the prototype of the wizard
        prototypes = self.env[active_model].browse(
            [self._context.get('active_id')])

        zip_details = self.zip_files(wizard, prototypes)

        if len(prototypes) == 1:
            zip_name = prototypes[0].name
        else:
            zip_name = "prototyper_export"

        wizard.write({
            'name':
            '{}.zip'.format(zip_name),
            'state':
            'get',
            'data':
            base64.encodestring(zip_details.stringIO.getvalue())
        })

        return {
            'type': 'ir.actions.act_window',
            'res_model': 'module_prototyper.module.export',
            'view_mode': 'form',
            'view_type': 'form',
            'res_id': wizard.id,
            'views': [(False, 'form')],
            'target': 'new',
        }

    @staticmethod
    def zip_files(wizard, prototypes):
        """Takes a set of file and zips them.
        :param file_details: tuple (filename, file_content)
        :return: tuple (zip_file, stringIO)
        """
        zip_details = namedtuple('Zip_details', ['zip_file', 'stringIO'])
        out = StringIO.StringIO()

        with zipfile.ZipFile(out, 'w') as target:
            for prototype in prototypes:
                # setting the jinja environment.
                # They will help the program to find the template to render the
                # files with.
                prototype.set_jinja_env(wizard.api_version)

                # generate_files ask the prototype to investigate the input and
                # to generate the file templates according to it.  zip_files,
                # in another hand, put all the template files into a package
                # ready to be saved by the user.
                file_details = prototype.generate_files()
                for filename, file_content in file_details:
                    if isinstance(file_content, unicode):
                        file_content = file_content.encode('utf-8')
                    # Prefix all names with module technical name
                    filename = os.path.join(prototype.name, filename)
                    info = zipfile.ZipInfo(filename)
                    info.compress_type = zipfile.ZIP_DEFLATED
                    info.external_attr = 2175008768  # specifies mode 0644
                    target.writestr(info, file_content)

            return zip_details(zip_file=target, stringIO=out)
Example #24
0
class event_track(models.Model):
    _name = "event.track"
    _description = 'Event Track'
    _order = 'priority, date'
    _inherit = [
        'mail.thread', 'ir.needaction_mixin', 'website.seo.metadata',
        'website.published.mixin'
    ]

    name = fields.Char('Title', required=True, translate=True)
    user_id = fields.Many2one('res.users',
                              'Responsible',
                              track_visibility='onchange',
                              default=lambda self: self.env.user)
    partner_id = fields.Many2one('res.partner', 'Proposed by')
    partner_name = fields.Char('Partner Name')
    partner_email = fields.Char('Partner Email')
    partner_phone = fields.Char('Partner Phone')
    partner_biography = fields.Html('Partner Biography')
    speaker_ids = fields.Many2many('res.partner', string='Speakers')
    tag_ids = fields.Many2many('event.track.tag', string='Tags')
    state = fields.Selection([('draft', 'Proposal'),
                              ('confirmed', 'Confirmed'),
                              ('announced', 'Announced'),
                              ('published', 'Published'),
                              ('refused', 'Refused'), ('cancel', 'Cancelled')],
                             'Status',
                             default='draft',
                             required=True,
                             copy=False,
                             track_visibility='onchange')
    description = fields.Html('Track Description', translate=True)
    date = fields.Datetime('Track Date')
    duration = fields.Float('Duration', digits=(16, 2), default=1.5)
    location_id = fields.Many2one('event.track.location', 'Room')
    event_id = fields.Many2one('event.event', 'Event', required=True)
    color = fields.Integer('Color Index')
    priority = fields.Selection([('0', 'Low'), ('1', 'Medium'), ('2', 'High'),
                                 ('3', 'Highest')],
                                'Priority',
                                required=True,
                                default='1')
    image = fields.Binary('Image',
                          compute='_compute_image',
                          store=True,
                          attachment=True)

    @api.one
    @api.depends('speaker_ids.image')
    def _compute_image(self):
        if self.speaker_ids:
            self.image = self.speaker_ids[0].image
        else:
            self.image = False

    @api.model
    def create(self, vals):
        res = super(event_track, self).create(vals)
        res.message_subscribe(res.speaker_ids.ids)
        res.event_id.message_post(body="""<h3>%(header)s</h3>
<ul>
    <li>%(proposed_by)s</li>
    <li>%(mail)s</li>
    <li>%(phone)s</li>
    <li>%(title)s</li>
    <li>%(speakers)s</li>
    <li>%(introduction)s</li>
</ul>""" % {
            'header':
            _('New Track Proposal'),
            'proposed_by':
            '<b>%s</b>: %s' %
            (_('Proposed By'),
             (res.partner_id.name or res.partner_name or res.partner_email)),
            'mail':
            '<b>%s</b>: %s' % (_('Mail'), '<a href="mailto:%s">%s</a>' %
                               (res.partner_email, res.partner_email)),
            'phone':
            '<b>%s</b>: %s' % (_('Phone'), res.partner_phone),
            'title':
            '<b>%s</b>: %s' % (_('Title'), res.name),
            'speakers':
            '<b>%s</b>: %s' % (_('Speakers Biography'), res.partner_biography),
            'introduction':
            '<b>%s</b>: %s' % (_('Talk Introduction'), res.description),
        },
                                  subtype='event.mt_event_track')
        return res

    @api.multi
    def write(self, vals):
        if vals.get('state') == 'published':
            vals.update({'website_published': True})
        res = super(event_track, self).write(vals)
        if vals.get('speaker_ids'):
            self.message_subscribe([
                speaker['id'] for speaker in self.resolve_2many_commands(
                    'speaker_ids', vals['speaker_ids'], ['id'])
            ])
        return res

    @api.multi
    @api.depends('name')
    def _website_url(self, field_name, arg):
        res = super(event_track, self)._website_url(field_name, arg)
        res.update({
            (track.id,
             '/event/%s/track/%s' % (slug(track.event_id), slug(track)))
            for track in self
        })
        return res

    def read_group(self,
                   cr,
                   uid,
                   domain,
                   fields,
                   groupby,
                   offset=0,
                   limit=None,
                   context=None,
                   orderby=False,
                   lazy=True):
        """ Override read_group to always display all states. """
        if groupby and groupby[0] == "state":
            # Default result structure
            # states = self._get_state_list(cr, uid, context=context)
            states = [('draft', 'Proposal'), ('confirmed', 'Confirmed'),
                      ('announced', 'Announced'), ('published', 'Published'),
                      ('cancel', 'Cancelled')]
            read_group_all_states = [{
                '__context': {
                    'group_by': groupby[1:]
                },
                '__domain':
                domain + [('state', '=', state_value)],
                'state':
                state_value,
                'state_count':
                0,
            } for state_value, state_name in states]
            # Get standard results
            read_group_res = super(event_track,
                                   self).read_group(cr,
                                                    uid,
                                                    domain,
                                                    fields,
                                                    groupby,
                                                    offset=offset,
                                                    limit=limit,
                                                    context=context,
                                                    orderby=orderby)
            # Update standard results with default results
            result = []
            for state_value, state_name in states:
                res = filter(lambda x: x['state'] == state_value,
                             read_group_res)
                if not res:
                    res = filter(lambda x: x['state'] == state_value,
                                 read_group_all_states)
                if state_value == 'cancel':
                    res[0]['__fold'] = True
                res[0]['state'] = [state_value, state_name]
                result.append(res[0])
            return result
        else:
            return super(event_track, self).read_group(cr,
                                                       uid,
                                                       domain,
                                                       fields,
                                                       groupby,
                                                       offset=offset,
                                                       limit=limit,
                                                       context=context,
                                                       orderby=orderby)

    def open_track_speakers_list(self, cr, uid, track_id, context=None):
        track_id = self.browse(cr, uid, track_id, context=context)
        return {
            'name':
            _('Speakers'),
            'domain':
            [('id', 'in', [partner.id for partner in track_id.speaker_ids])],
            'view_type':
            'form',
            'view_mode':
            'kanban,form',
            'res_model':
            'res.partner',
            'view_id':
            False,
            'type':
            'ir.actions.act_window',
        }
Example #25
0
class hr_employee(models.Model):
    '''
    实体:员工档案,字段扩展
    '''
    _inherit = 'hr.employee'
    educatio_type = [
        ('middle_school', '初中'),
        ('high_school', '高中'),
        ('diploma', '大专'),
        ('benco', '本科'),
        ('master', '硕士研究生'),
        ('doctor', '博士研究生'),
    ]
    degrees_type = [
        ('middle_school', '学士'),
        ('high_school', '硕士'),
        ('diploma', '博士'),
    ]
    training_methods_type = [
        ('series_incur', '统招'),
        ('self-taught', '自考'),
        ('adult_education', '成教'),
        ('tv_university', '电大&夜大'),
    ]
    category_type = [
        ('OM', '管理'),
        ('A', '职能'),
        ('S', '销售'),
        ('CRTE', '技术'),
    ]
    job_titile_type = [
        ('O', '经营管理'),
        ('M', '一般管理'),
        ('A', '行政职能'),
        ('S', '业务营销'),
    ]
    marriage_type = [
        ('unmarried', '未婚未育'),
        ('unmarried_pregnant', '未婚已育'),
        ('married', '已婚未育'),
        ('married_pregnant', '已婚已育'),
    ]
    household_type = [
        ('city_town', '本市城镇'),
        ('city_village', '本市农业'),
        ('other_town', '外埠城镇'),
        ('other_village', '外埠农业'),
    ]
    learning_style = [
        ('full_time', '脱产'),
        ('part_time', '半脱产'),
        ('job', '在职'),
    ]
    employment_type = [
        ('internship', '实习'),
        ('trial', '试用'),
        ('formally', '正式'),
        ('employ', '聘用(含劳务)'),
    ]

    service_state_type = [
        ('entry_employee', '入职员工'),
        ('regular_employee', '转正员工'),
        ('mobilize_employee', '调动员工'),
        ('leave_employee', '离职员工'),
    ]

    code = fields.Char(string='员工代号')
    country_id = fields.Many2one(
        'res.country',
        string="国家",
        default=lambda self: _country_get(self, self.env.cr, self.env.user.id))
    province_id = fields.Many2one('res.country.state', string="省份")
    university = fields.Char(string='毕业院校')
    educatio = fields.Selection(educatio_type, string='学历')
    degrees = fields.Selection(degrees_type, string='学位')
    profession = fields.Char(string='专业')
    training_methods = fields.Selection(training_methods_type, string='培养方式')
    admission_date = fields.Date(string='入学日期')
    graduation_date = fields.Date(string='毕业日期')
    ext = fields.Char(string='分机号')

    category = fields.Selection(category_type, string='职类')
    job_titile = fields.Selection(job_titile_type, string='职别')
    grade = fields.Char(string='职等')
    marriage = fields.Selection(marriage_type, string='婚育')
    household = fields.Selection(household_type, string='户口性质')
    learning = fields.Selection(learning_style, string='学习方式')
    #其他页签
    urgent_contacts = fields.Char(string='紧急联系人')
    urgent_contacts_mobile = fields.Char(string='紧急联系人手机')
    urgent_contacts_phone = fields.Char(string='紧急联系人座机')
    urgent_contacts_relation = fields.Char(string='紧急联系人关系')

    #用工类型
    employment = fields.Selection(employment_type,
                                  string='用工类型',
                                  default='formally')
    induction_date = fields.Date(string='入职日期')
    formally_date = fields.Date(string='转正日期')
    exit_date = fields.Date(string='离职日期')

    #社保信息
    social_insurance_years = fields.Integer(string='社保缴费年',
                                            help='填写社保机构出具或查询到的社保缴费年数')
    social_insurance_months = fields.Integer(string='社保缴费月',
                                             help='填写社保机构出具或查询到的社保缴费月数')

    #工龄
    working_years_prove = fields.Binary(string='工龄证明')
    pre_job_seniority = fields.Float(
        digits=(16, 2),
        string='入职前工龄',
        help='根据入职前提供的“社保缴费年限”证明或者存档机构出具的“工龄证明”核定入职前工龄,一经核定不允许修改')
    company_years = fields.Float(compute='_compute_company_years', string='司龄')
    now_working_years = fields.Float(compute='_methods_now_working_years',
                                     string='现工龄')

    nation = fields.Char(string='民族')
    birthday = fields.Date(compute="_compute_birthday",
                           string='出生日期',
                           store=True)
    age = fields.Integer(compute="_compute_ages", string='年龄', store=True)
    service_state = fields.Selection(service_state_type,
                                     string='在职状态',
                                     default='entry_employee')

    # contract_id=fields.Many2one('hr.contract',string='现合同')

    def _caltime(self, date1, date2):
        date1 = time.strptime(date1, "%Y-%m-%d")
        date2 = time.strptime(date2, "%Y-%m-%d")
        date1 = datetime.datetime(date1[0], date1[1], date1[2])
        date2 = datetime.datetime(date2[0], date2[1], date2[2])
        return date2 - date1

    @api.one
    @api.depends('induction_date', 'service_state', 'exit_date')
    def _compute_company_years(self):
        '''
        根据入职日期计算司
        :return:
        '''
        current_date = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        end_date = current_date
        if self.service_state == 'leave_employee' and self.exit_date:
            end_date = self.exit_date
        if self.induction_date:
            self.company_years = self._caltime(self.induction_date,
                                               end_date).days / 365.00
        else:
            self.company_years = 0.00

    @api.one
    @api.depends('pre_job_seniority', 'company_years')
    def _methods_now_working_years(self):
        '''
        根据入职前工龄与司龄计算现工龄
        :return:
        '''
        self.now_working_years = self.pre_job_seniority + self.company_years

    @api.one
    @api.depends('identification_id')
    def _compute_birthday(self):
        '''
        根据身份证号计算出生日期
        :return:
        '''
        year = ''
        month = ''
        day = ''
        if self.identification_id:
            if len(self.identification_id) == 18:
                year = self.identification_id[6:10]
                month = self.identification_id[10:12]
                day = self.identification_id[12:14]

            if len(self.identification_id) == 16:
                year = '19' + self.identification_id[6:8]
                month = self.identification_id[8:10]
                day = self.identification_id[10:12]
            str_birthday = "%s-%s-%s" % (year, month, day)
            self.birthday = datetime.datetime(int(year), int(month), int(day))
            # self._compute_ages()

    @api.one
    @api.depends('birthday')
    def _compute_ages(self):
        '''
        根据出生日期计算年龄
        :return:
        '''
        current_date = time.strftime('%Y-%m-%d', time.localtime(time.time()))
        if self.birthday:
            self.age = self._caltime(self.birthday, current_date).days / 365
        else:
            self.age = 0

    @api.model
    def create(self, vals):
        '''
        功能:员工代号未维护,找编码规则;维护了则验证是否重复
        :param vals:
        :return:
        '''
        code = vals.get('code', False)
        if not code:
            vals['code'] = self.env['ir.sequence'].get('hr.employee')
        else:
            exis_emps = self.env['hr.employee'].search([('code', '=', code),
                                                        ('id', '!=', self.id)])
            if len(exis_emps) > 0:
                exis_emp_names = ''
                for exis_emp in exis_emps:
                    exis_emp_names = exis_emp_names + exis_emp.name + ','
                msg = '员工代号%s重复(具有该员工代号的员工有%s)!' % (code, exis_emp_names[0:-1])
                raise except_osv(_('错误'), _(msg))

        return super(hr_employee, self).create(vals)

    @api.multi
    def write(self, vals):
        '''
        功能:员工代号不能重复
        :param vals:
        :return:
        '''
        try:
            for employee in self:
                code = vals.get('code', False)
                if code:
                    exis_emps = self.env['hr.employee'].search([
                        ('code', '=', code), ('id', '!=', employee.id)
                    ])
                    if len(exis_emps) > 0:
                        exis_emp_names = ''
                        for exis_emp in exis_emps:
                            exis_emp_names = exis_emp_names + exis_emp.name + ','
                        msg = '员工代号%s重复(具有该员工代号的员工有%s)!' % (
                            code, exis_emp_names[0:-1])
                        raise except_osv(_('错误'), _(msg))
                return super(hr_employee, employee).write(vals)

        except Exception as e:
            _logger.error(e)
            raise e
Example #26
0
class customized_invoice(models.Model):
    _inherit = ["account.invoice"]

    @api.model
    def _default_template(self):
        company_obj = self.env['res.company']
        company = self.env['res.users'].browse([self.env.user.id]).company_id
        if not company.template_invoice:
            def_tpl = self.env['ir.ui.view'].search(
                [('key', 'like', 'customized_invoice.template_%'),
                 ('type', '=', 'qweb')],
                order='id asc',
                limit=1)
            company.write({'template_invoice': def_tpl.id})
        return company.template_invoice or self.env.ref(
            'account.report_invoice_document')

    @api.model
    def _default_odd(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).odd

    @api.model
    def _default_even(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).even

    @api.model
    def _default_theme_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).theme_color

    @api.model
    def _default_theme_txt_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).theme_txt_color

    @api.model
    def _default_name_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).name_color

    @api.model
    def _default_cust_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).cust_color

    @api.model
    def _default_text_color(self):
        company_id = self.env['res.users'].browse([self.env.user.id
                                                   ]).company_id
        return self.env['res.company'].browse([company_id.id]).text_color

    invoice_logo = fields.Binary(
        "Logo",
        attachment=True,
        help=
        "This field holds the image used as logo for the invoice, if non is uploaded, the default logo define in the copmany settings will be used"
    )
    template_id = fields.Many2one(
        'ir.ui.view',
        'Invoice Template',
        default=_default_template,
        required=True,
        domain=
        "[('type', '=', 'qweb'), ('key', 'like', 'customized_invoice.template\_%\_document' )]"
    )
    odd = fields.Char('Odd parity Color',
                      size=7,
                      required=True,
                      default=_default_odd,
                      help="The background color for Odd lines in the invoice")
    even = fields.Char(
        'Even parity Color',
        size=7,
        required=True,
        default=_default_even,
        help="The background color for Even lines in the invoice")
    theme_color = fields.Char(
        'Theme Color',
        size=7,
        required=True,
        default=_default_theme_color,
        help="The Main Theme color of the invoice. Normally this\
			 should be one of your official company colors")
    theme_txt_color = fields.Char(
        'Theme Text Color',
        size=7,
        required=True,
        default=_default_theme_txt_color,
        help=
        "The Text color of the areas with theme color. This should not be the same the theme color"
    )
    text_color = fields.Char(
        'Text Color',
        size=7,
        required=True,
        default=_default_text_color,
        help="The Text color of the invoice. Normally this\
			 should be one of your official company colors or default HTML text color")
    name_color = fields.Char('Company Name Color',
                             size=7,
                             required=True,
                             default=_default_name_color,
                             help="The Text color of the Company Name. \
			Normally thisshould be one of your official company colors or default HTML text color"
                             )
    cust_color = fields.Char('Customer Name Color',
                             size=7,
                             required=True,
                             default=_default_name_color,
                             help="The Text color of the Customer Name. \
			Normally this should be one of your official company colors or default HTML text color"
                             )

    ##Override invoice_print method in original invoice class in account module
    @api.multi
    def invoice_print(self):
        """ Print the invoice and mark it as sent, so that we can see more
               easily the next step of the workflow
	       This Method overrides the one in the original invoice class
            """
        self.ensure_one()
        self.sent = True
        return self.env['report'].get_action(self,
                                             'customized_invoice.my_invoice')
Example #27
0
class SaasPortalPlan(models.Model):
    _name = 'saas_portal.plan'

    name = fields.Char('Plan', required=True)
    summary = fields.Char('Summary')
    template_id = fields.Many2one('saas_portal.database', 'Template', ondelete='restrict')
    demo = fields.Boolean('Install Demo Data')
    maximum_allowed_dbs_per_partner = fields.Integer(help='maximum allowed non-trial databases per customer',
                                                     require=True, default=0)
    maximum_allowed_trial_dbs_per_partner = fields.Integer(help='maximum allowed trial databases per customer',
                                                           require=True, default=0)

    max_users = fields.Char('Initial Max users', default='0')
    total_storage_limit = fields.Integer('Total storage limit (MB)')
    block_on_expiration = fields.Boolean('Block clients on expiration', default=False)
    block_on_storage_exceed = fields.Boolean('Block clients on storage exceed', default=False)

    def _get_default_lang(self):
        return self.env.lang

    def _default_tz(self):
        return self.env.user.tz

    lang = fields.Selection(scan_languages(), 'Language', default=_get_default_lang)
    tz = fields.Selection(_tz_get, 'TimeZone', default=_default_tz)
    sequence = fields.Integer('Sequence')
    state = fields.Selection([('draft', 'Draft'), ('confirmed', 'Confirmed')],
                             'State', compute='_get_state', store=True)
    expiration = fields.Integer('Expiration (hours)', help='time to delete database. Use for demo')
    _order = 'sequence'

    dbname_template = fields.Char('DB Names',
                                  help='Used for generating client database domain name. Use %i for numbering. Ignore if you use manually created db names',
                                  placeholder='crm-%i.yuancloud.com')
    server_id = fields.Many2one('saas_portal.server', string='SaaS Server',
                                ondelete='restrict',
                                help='User this saas server or choose random')

    website_description = fields.Html('Website description')
    logo = fields.Binary('Logo')

    @api.one
    @api.depends('template_id.state')
    def _get_state(self):
        if self.template_id.state == 'template':
            self.state = 'confirmed'
        else:
            self.state = 'draft'

    @api.one
    def _new_database_vals(self, vals):
        vals['max_users'] = self.max_users
        vals['total_storage_limit'] = self.total_storage_limit
        vals['block_on_expiration'] = self.block_on_expiration
        vals['block_on_storage_exceed'] = self.block_on_storage_exceed
        return vals

    @api.multi
    def create_new_database(self, **kwargs):
        return self._create_new_database(**kwargs)

    @api.multi
    def _create_new_database(self, dbname=None, client_id=None, partner_id=None, user_id=None, notify_user=False,
                             trial=False, support_team_id=None, async=None):
        self.ensure_one()

        server = self.server_id
        if not server:
            server = self.env['saas_portal.server'].get_saas_server()

        server.action_sync_server()
        if not partner_id and user_id:
            user = self.env['res.users'].browse(user_id)
            partner_id = user.partner_id.id

        if not trial and self.maximum_allowed_dbs_per_partner != 0:
            db_count = self.env['saas_portal.client'].search_count([('partner_id', '=', partner_id),
                                                                    ('state', '=', 'open'),
                                                                    ('plan_id', '=', self.id),
                                                                    ('trial', '=', False)])
            if db_count >= self.maximum_allowed_dbs_per_partner:
                raise MaximumDBException("Limit of databases for this plan is %(maximum)s reached" % {
                    'maximum': self.maximum_allowed_dbs_per_partner})
        if trial and self.maximum_allowed_trial_dbs_per_partner != 0:
            trial_db_count = self.env['saas_portal.client'].search_count([('partner_id', '=', partner_id),
                                                                          ('state', '=', 'open'),
                                                                          ('plan_id', '=', self.id),
                                                                          ('trial', '=', True)])
            if trial_db_count >= self.maximum_allowed_trial_dbs_per_partner:
                raise MaximumTrialDBException("Limit of trial databases for this plan is %(maximum)s reached" % {
                    'maximum': self.maximum_allowed_trial_dbs_per_partner})

        vals = {'name': dbname or self.generate_dbname()[0],
                'server_id': server.id,
                'plan_id': self.id,
                'partner_id': partner_id,
                'trial': trial,
                'support_team_id': support_team_id,
                }
        client = None
        if client_id:
            vals['client_id'] = client_id
            client = self.env['saas_portal.client'].search([('client_id', '=', client_id)])

        vals = self._new_database_vals(vals)[0]

        if client:
            client.write(vals)
        else:
            client = self.env['saas_portal.client'].create(vals)
        client_id = client.client_id

        scheme = server.request_scheme
        port = server.request_port
        if user_id:
            owner_user = self.env['res.users'].browse(user_id)
        else:
            owner_user = self.env.user
        owner_user_data = {
            'user_id': owner_user.id,
            'login': owner_user.login,
            'name': owner_user.name,
            'email': owner_user.email,
        }
        trial_expiration_datetime = (datetime.strptime(client.create_date,
                                                       DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(
            hours=self.expiration)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)  # for trial
        state = {
            'd': client.name,
            'e': trial and trial_expiration_datetime or client.create_date,
            'r': '%s://%s:%s/web' % (scheme, client.name, port),
            'owner_user': owner_user_data,
            't': client.trial,
            'addons': [addon.technical_name for addon in client.plan_id.app_store_module_ids]
        }
        if self.template_id:
            state.update({'db_template': self.template_id.name})
        scope = ['userinfo', 'force_login', 'trial', 'skiptheuse']
        url = server._request_server(path='/saas_server/new_database',
                                     scheme=scheme,
                                     port=port,
                                     state=state,
                                     client_id=client_id,
                                     scope=scope, )[0]
        res = requests.get(url, verify=(self.server_id.request_scheme == 'https' and self.server_id.verify_ssl))
        if res.status_code != 200:
            # TODO /saas_server/new_database show more details here
            raise exceptions.Warning('Error %s' % res.status_code)
        data = simplejson.loads(res.text)
        params = {
            'state': data.get('state'),
            'access_token': client.oauth_application_id._get_access_token(user_id, create=True),
        }
        url = '{url}?{params}'.format(url=data.get('url'), params=werkzeug.url_encode(params))

        # send email
        if notify_user:
            template = self.env.ref('saas_portal.email_template_create_saas')
            client.message_post_with_template(template.id, composition_mode='comment')

        if trial:
            client.expiration_datetime = trial_expiration_datetime
        client.send_params_to_client_db()
        client.server_id.action_sync_server()

        return {'url': url, 'id': client.id, 'client_id': client_id}
Example #28
0
class ModulePrototyper(models.Model):
    """Module Prototyper gathers different information from all over the
    database to build a prototype of module.
    We are calling it a prototype as it will most likely need to be reviewed
    by a developer to fix glitch that would sneak it during the generation of
    files but also to add not supported features.
    """
    _name = "module_prototyper"
    _description = "Module Prototyper"

    def get_default_description(self):
        """
        Extract the content of default description
        """
        filepath = '{}/../data/README.rst'.format(os.path.dirname(__file__))
        with open(filepath, 'r') as content_file:
            content = content_file.read()
        return content

    license = fields.Selection(
        [(licenses.GPL3, 'GPL Version 3'),
         (licenses.GPL3_L, 'GPL-3 or later version'),
         (licenses.LGPL3, 'LGPL-3'),
         (licenses.LGPL3_L, 'LGPL-3 or later version'),
         (licenses.AGPL3, 'Affero GPL-3'),
         (licenses.OSI, 'Other OSI Approved Licence'),
         ('Other proprietary', 'Other Proprietary')],
        string='License',
        default=licenses.AGPL3,
    )
    name = fields.Char(
        'Technical Name',
        required=True,
        help=('The technical name will be used to define the name of '
              'the exported module, the name of the model.'))
    category_id = fields.Many2one('ir.module.category', 'Category')
    human_name = fields.Char(
        'Module Name',
        required=True,
        help=('The Module Name will be used as the displayed name of the '
              'exported module.'))
    summary = fields.Char('Summary',
                          required=True,
                          help=('Enter a summary of your module'))
    description = fields.Text(
        'Description',
        required=True,
        help=('Enter the description of your module, what it does, how to '
              'install, configure and use it, the roadmap or known issues. '
              'The description will be exported in README.rst'),
        default=get_default_description)
    author = fields.Char('Author', required=True, help=('Enter your name'))
    maintainer = fields.Char(
        'Maintainer',
        help=('Enter the name of the person or organization who will '
              'maintain this module'))
    website = fields.Char('Website', help=('Enter the URL of your website'))
    icon_image = fields.Binary(
        'Icon',
        help=('The icon set up here will be used as the icon '
              'for the exported module also'))
    version = fields.Char(
        'Version',
        size=9,
        default='5.0.1.0.0',
        help=('Enter the version of your module with 5 digits'))
    auto_install = fields.Boolean(
        'Auto Install',
        default=False,
        help='Check if the module should be install by default.')
    application = fields.Boolean(
        'Application',
        default=False,
        help='Check if the module is an YuanCloud application.')
    # Relations
    dependency_ids = fields.Many2many(
        'ir.module.module',
        'module_prototyper_module_rel',
        'module_prototyper_id',
        'module_id',
        'Dependencies',
        help=('Enter the list of required modules that need to be installed '
              'for your module to work properly'))
    data_ids = fields.Many2many(
        'ir.filters',
        'prototype_data_rel',
        'module_prototyper_id',
        'filter_id',
        'Data filters',
        help="The records matching the filters will be added as data.")
    demo_ids = fields.Many2many(
        'ir.filters',
        'prototype_demo_rel',
        'module_prototyper_id',
        'filter_id',
        'Demo filters',
        help="The records matching the filters will be added as demo data.")
    field_ids = fields.Many2many(
        'ir.model.fields',
        'prototype_fields_rel',
        'module_prototyper_id',
        'field_id',
        'Fields',
        help=('Enter the list of fields that you have created or modified '
              'and want to export in this module. New models will be '
              'exported as long as you choose one of his fields.'))
    menu_ids = fields.Many2many(
        'ir.ui.menu',
        'prototype_menu_rel',
        'module_prototyper_id',
        'menu_id',
        'Menu Items',
        help=('Enter the list of menu items that you have created and want '
              'to export in this module. Related windows actions will be '
              'exported as well.'))
    view_ids = fields.Many2many(
        'ir.ui.view',
        'prototype_view_rel',
        'module_prototyper_id',
        'view_id',
        'Views',
        help=('Enter the list of views that you have created and want to '
              'export in this module.'))
    group_ids = fields.Many2many(
        'res.groups',
        'prototype_groups_rel',
        'module_prototyper_id',
        'group_id',
        'Groups',
        help=('Enter the list of groups that you have created and want to '
              'export in this module.'))
    right_ids = fields.Many2many(
        'ir.model.access',
        'prototype_rights_rel',
        'module_prototyper_id',
        'right_id',
        'Access Rights',
        help=('Enter the list of access rights that you have created and '
              'want to export in this module.'))
    rule_ids = fields.Many2many(
        'ir.rule',
        'prototype_rule_rel',
        'module_prototyper_id',
        'rule_id',
        'Record Rules',
        help=('Enter the list of record rules that you have created and '
              'want to export in this module.'))
    report_ids = fields.Many2many(
        'ir.actions.report.xml',
        'prototype_report_rel',
        'module_prototyper_id',
        'report_id',
        'Reports',
        help=('Enter the list of reports that you have created and '
              'want to export in this module.'))
    activity_ids = fields.Many2many(
        'workflow.activity',
        'prototype_wf_activity_rel',
        'module_prototyper_id',
        'activity_id',
        'Activities',
        help=('Enter the list of workflow activities that you have created '
              'and want to export in this module'))
    transition_ids = fields.Many2many(
        'workflow.transition',
        'prototype_wf_transition_rel',
        'module_prototyper_id',
        'transition_id',
        'Transitions',
        help=('Enter the list of workflow transitions that you have created '
              'and want to export in this module'))

    _env = None
    _data_files = ()
    _demo_files = ()
    _field_descriptions = None
    File_details = namedtuple('file_details', ['filename', 'filecontent'])
    template_path = '{}/../templates/'.format(os.path.dirname(__file__))

    @api.model
    def set_jinja_env(self, api_version):
        """Set the Jinja2 environment.
        The environment will helps the system to find the templates to render.
        :param api_version: string, yuancloud api
        :return: jinja2.Environment instance.
        """
        if self._env is None:
            self._env = Environment(lstrip_blocks=True,
                                    trim_blocks=True,
                                    loader=FileSystemLoader(
                                        os.path.join(self.template_path,
                                                     api_version)))
        return self._env

    def set_field_descriptions(self):
        """Mock the list of fields into dictionary.
        It allows us to add or change attributes of the fields.

        :return: None
        """
        for field in self.field_ids:
            field_description = {}
            # This will mock a field record.
            # the mock will allow us to add data or modify the data
            # of the field (like for the name) with keeping all the
            # attributes of the record.
            field_description.update({
                attr_name: getattr(field, attr_name)
                for attr_name in dir(field) if not attr_name[0] == '_'
            })
            field_description['name'] = self.unprefix(field.name)
            self._field_descriptions[field] = field_description

    @api.model
    def generate_files(self):
        """ Generates the files from the details of the prototype.
        :return: tuple
        """
        assert self._env is not None, \
            'Run set_env(api_version) before to generate files.'

        # Avoid sharing these across instances
        self._data_files = []
        self._demo_files = []
        self._field_descriptions = {}
        self.set_field_descriptions()
        file_details = []
        file_details.extend(self.generate_models_details())
        file_details.extend(self.generate_views_details())
        file_details.extend(self.generate_menus_details())
        file_details.append(self.generate_module_init_file_details())
        file_details.extend(self.generate_data_files())
        # must be the last as the other generations might add information
        # to put in the __yuancloud__: additional dependencies, views files, etc.
        file_details.append(self.generate_module_yuancloud_file_details())
        if self.icon_image:
            file_details.append(self.save_icon())

        return file_details

    @api.model
    def save_icon(self):
        """Save the icon of the prototype as a image.
        The image is used afterwards as the icon of the exported module.

        :return: FileDetails instance
        """
        # TODO: The image is not always a jpg.
        # 2 ways to do it:
        #   * find a way to detect image type from the data
        #   * add document as a dependency.
        # The second options seems to be better, as Document is a base module.
        return self.File_details(
            os.path.join('static', 'description', 'icon.jpg'),
            base64.b64decode(self.icon_image))

    @api.model
    def generate_module_yuancloud_file_details(self):
        """Wrapper to generate the __yuancloud__.py file of the module."""
        return self.generate_file_details(
            '__yuancloud__.py',
            '__yuancloud__.py.template',
            prototype=self,
            data_files=self._data_files,
            demo_fiels=self._demo_files,
        )

    @api.model
    def generate_module_init_file_details(self):
        """Wrapper to generate the __init__.py file of the module."""
        return self.generate_file_details(
            '__init__.py',
            '__init__.py.template',
            # no import models if no work of fields in
            # the prototype
            models=bool(self.field_ids))

    @api.model
    def generate_models_details(self):
        """
        Finds the models from the list of fields and generates
        the __init__ file and each models files (one by class).
        """
        files = []
        # TODO: doesn't work as need to find the module to import
        # and it is not necessary the name of the model the fields
        # belongs to.
        # ie. field.cell_phone is defined in a model inheriting from
        # res.partner.
        # How do we find the module the field was defined in?
        # dependencies = set([dep.id for dep in self.dependencies])

        relations = {}
        field_descriptions = self._field_descriptions or {}
        for field in field_descriptions.itervalues():
            model = field.get('model_id')
            relations.setdefault(model, []).append(field)
            # dependencies.add(model.id)

        # blind update of dependencies.
        # self.write({
        #     'dependencies': [(6, 0, [id_ for id_ in dependencies])]
        # })

        files.append(self.generate_models_init_details(relations.keys()))
        for model, custom_fields in relations.iteritems():
            files.append(self.generate_model_details(model, custom_fields))

        return files

    @api.model
    def generate_models_init_details(self, ir_models):
        """Wrapper to generate the __init__.py file in models folder."""
        return self.generate_file_details(
            'models/__init__.py',
            'models/__init__.py.template',
            models=[
                self.friendly_name(ir_model.model) for ir_model in ir_models
            ])

    @api.model
    def generate_views_details(self):
        """Wrapper to generate the views files."""
        relations = {}
        for view in self.view_ids:
            relations.setdefault(view.model, []).append(view)

        views_details = []
        for model, views in relations.iteritems():
            filepath = 'views/{}_view.xml'.format(
                self.friendly_name(self.unprefix(model)))
            views_details.append(
                self.generate_file_details(filepath,
                                           'views/model_views.xml.template',
                                           views=views))
            self._data_files.append(filepath)

        return views_details

    @api.model
    def generate_menus_details(self):
        """Wrapper to generate the menus files."""
        relations = {}
        for menu in self.menu_ids:
            if menu.action and menu.action.res_model:
                model = self.unprefix(menu.action.res_model)
            else:
                model = 'ir_ui'
            relations.setdefault(model, []).append(menu)

        menus_details = []
        for model_name, menus in relations.iteritems():
            model_name = self.unprefix(model_name)
            filepath = 'views/{}_menus.xml'.format(
                self.friendly_name(model_name))
            menus_details.append(
                self.generate_file_details(
                    filepath,
                    'views/model_menus.xml.template',
                    menus=menus,
                ))
            self._data_files.append(filepath)

        return menus_details

    @api.model
    def generate_model_details(self, model, field_descriptions):
        """Wrapper to generate the python file for the model.

        :param model: ir.model record.
        :param field_descriptions: list of ir.model.fields records.
        :return: FileDetails instance.
        """
        python_friendly_name = self.friendly_name(self.unprefix(model.model))
        return self.generate_file_details(
            'models/{}.py'.format(python_friendly_name),
            'models/model_name.py.template',
            name=python_friendly_name,
            model=model,
            fields=field_descriptions,
        )

    @api.model
    def generate_data_files(self):
        """ Generate data and demo files """
        data, demo = {}, {}
        filters = [(data, ir_filter) for ir_filter in self.data_ids
                   ] + [(demo, ir_filter) for ir_filter in self.demo_ids]

        for target, ir_filter in filters:
            model = ir_filter.model_id
            model_obj = self.env[model]
            target.setdefault(model, model_obj.browse([]))
            target[model] |= model_obj.search(safe_eval(ir_filter.domain))

        res = []
        for prefix, model_data, file_list in [('data', data, self._data_files),
                                              ('demo', demo, self._demo_files)
                                              ]:
            for model_name, records in model_data.iteritems():
                fname = self.friendly_name(self.unprefix(model_name))
                filename = '{0}/{1}.xml'.format(prefix, fname)
                self._data_files.append(filename)

                res.append(
                    self.generate_file_details(
                        filename,
                        'data/model_name.xml.template',
                        model=model_name,
                        records=records,
                    ))

        return res

    @classmethod
    def unprefix(cls, name):
        if not name:
            return name
        return re.sub('^x_', '', name)

    @classmethod
    def is_prefixed(cls, name):
        return bool(re.match('^x_', name))

    @classmethod
    def friendly_name(cls, name):
        return name.replace('.', '_')

    @classmethod
    def fixup_domain(cls, domain):
        """ Fix a domain according to unprefixing of fields """
        res = []
        for elem in domain:
            if len(elem) == 3:
                elem = list(elem)
                elem[0] = cls.unprefix(elem[0])
            res.append(elem)
        return res

    @classmethod
    def fixup_arch(cls, archstr):
        doc = lxml.etree.fromstring(archstr)
        for elem in doc.xpath("//*[@name]"):
            elem.attrib["name"] = cls.unprefix(elem.attrib["name"])

        for elem in doc.xpath("//*[@attrs]"):
            try:
                attrs = safe_eval(elem.attrib["attrs"])
            except Exception:
                _logger.error("Unable to eval attribute: %s, skipping",
                              elem.attrib["attrs"])
                continue

            if isinstance(attrs, dict):
                for key, val in attrs.iteritems():
                    if isinstance(val, (list, tuple)):
                        attrs[key] = cls.fixup_domain(val)
                elem.attrib["attrs"] = repr(attrs)

        for elem in doc.xpath("//field"):
            # Make fields self-closed by removing useless whitespace
            if elem.text and not elem.text.strip():
                elem.text = None

        return lxml.etree.tostring(doc)

    @api.model
    def generate_file_details(self, filename, template, **kwargs):
        """ generate file details from jinja2 template.
        :param filename: name of the file the content is related to
        :param template: path to the file to render the content
        :param kwargs: arguments of the template
        :return: File_details instance
        """
        template = self._env.get_template(template)
        # keywords used in several templates.
        kwargs.update({
            'export_year': date.today().year,
            'author': self.author,
            'website': self.website,
            'license_text': licenses.get_license_text(self.license),
            'cr': self._cr,
            # Utility functions
            'fixup_arch': self.fixup_arch,
            'is_prefixed': self.is_prefixed,
            'unprefix': self.unprefix,
            'wrap': wrap,
        })
        return self.File_details(filename, template.render(kwargs))
Example #29
0
class Channel(models.Model):
    """ A mail.channel is a discussion group that may behave like a listener
    on documents. """
    _description = 'Discussion channel'
    _name = 'mail.channel'
    _mail_flat_thread = False
    _mail_post_access = 'read'
    _inherit = ['mail.thread']
    _inherits = {'mail.alias': 'alias_id'}

    def _get_default_image(self):
        image_path = modules.get_module_resource('mail', 'static/src/img',
                                                 'groupdefault.png')
        return tools.image_resize_image_big(
            open(image_path, 'rb').read().encode('base64'))

    name = fields.Char('Name', required=True, translate=True)
    channel_type = fields.Selection([('chat', 'Chat Discussion'),
                                     ('channel', 'Channel')],
                                    'Channel Type',
                                    default='channel')
    description = fields.Text('Description')
    uuid = fields.Char('UUID',
                       size=50,
                       select=True,
                       default=lambda self: '%s' % uuid.uuid4())
    email_send = fields.Boolean('Send messages by email', default=False)
    # multi users channel
    channel_last_seen_partner_ids = fields.One2many('mail.channel.partner',
                                                    'channel_id',
                                                    string='Last Seen')
    channel_partner_ids = fields.Many2many('res.partner',
                                           'mail_channel_partner',
                                           'channel_id',
                                           'partner_id',
                                           string='Listeners')
    channel_message_ids = fields.Many2many('mail.message',
                                           'mail_message_mail_channel_rel')
    message_is_follower = fields.Boolean(
        'Is a member', compute='_compute_message_is_follower')
    # access
    public = fields.Selection(
        [('public', 'Everyone'), ('private', 'Invited people only'),
         ('groups', 'Selected group of users')],
        'Privacy',
        required=True,
        default='groups',
        help=
        'This group is visible by non members. Invisible groups can add members through the invite button.'
    )
    group_public_id = fields.Many2one(
        'res.groups',
        string='Authorized Group',
        default=lambda self: self.env.ref('base.group_user'))
    group_ids = fields.Many2many(
        'res.groups',
        rel='mail_channel_res_group_rel',
        id1='mail_channel_id',
        id2='groups_id',
        string='Auto Subscription',
        help="Members of those groups will automatically added as followers. "
        "Note that they will be able to manage their subscription manually "
        "if necessary.")
    # image: all image fields are base64 encoded and PIL-supported
    image = fields.Binary(
        "Photo",
        default=_get_default_image,
        attachment=True,
        help=
        "This field holds the image used as photo for the group, limited to 1024x1024px."
    )
    image_medium = fields.Binary(
        'Medium-sized photo',
        compute='_get_image',
        inverse='_set_image_medium',
        store=True,
        attachment=True,
        help="Medium-sized photo of the group. It is automatically "
        "resized as a 128x128px image, with aspect ratio preserved. "
        "Use this field in form views or some kanban views.")
    image_small = fields.Binary(
        'Small-sized photo',
        compute='_get_image',
        inverse='_set_image_small',
        store=True,
        attachment=True,
        help="Small-sized photo of the group. It is automatically "
        "resized as a 64x64px image, with aspect ratio preserved. "
        "Use this field anywhere a small image is required.")
    alias_id = fields.Many2one(
        'mail.alias',
        'Alias',
        ondelete="restrict",
        required=True,
        help=
        "The email address associated with this group. New emails received will automatically create new topics."
    )

    @api.multi
    def _compute_message_is_follower(self):
        memberships = self.env['mail.channel.partner'].sudo().search([
            ('channel_id', 'in', self.ids),
            ('partner_id', '=', self.env.user.partner_id.id),
        ])
        membership_ids = memberships.mapped('channel_id')
        for record in self:
            record.message_is_follower = record in membership_ids

    @api.one
    @api.depends('image')
    def _get_image(self):
        self.image_medium = tools.image_resize_image_medium(self.image)
        self.image_small = tools.image_resize_image_small(self.image)

    def _set_image_medium(self):
        self.image = tools.image_resize_image_big(self.image_medium)

    def _set_image_small(self):
        self.image = tools.image_resize_image_big(self.image_small)

    @api.model
    def create(self, vals):
        # Create channel and alias
        channel = super(
            Channel,
            self.with_context(alias_model_name=self._name,
                              alias_parent_model_name=self._name,
                              mail_create_nolog=True,
                              mail_create_nosubscribe=True)).create(vals)
        channel.alias_id.write({
            "alias_force_thread_id": channel.id,
            'alias_parent_thread_id': channel.id
        })

        if vals.get('group_ids'):
            channel._subscribe_users()

        # make channel listen itself: posting on a channel notifies the channel
        if not self._context.get('mail_channel_noautofollow'):
            channel.message_subscribe(channel_ids=[channel.id])

        return channel

    @api.multi
    def unlink(self):
        aliases = self.mapped('alias_id')

        # Delete mail.channel
        try:
            all_emp_group = self.env.ref('mail.channel_all_employees')
        except ValueError:
            all_emp_group = None
        if all_emp_group and all_emp_group in self:
            raise UserError(
                _('You cannot delete those groups, as the Whole Company group is required by other modules.'
                  ))
        res = super(Channel, self).unlink()
        # Cascade-delete mail aliases as well, as they should not exist without the mail.channel.
        aliases.sudo().unlink()
        return res

    @api.multi
    def write(self, vals):
        result = super(Channel, self).write(vals)
        if vals.get('group_ids'):
            self._subscribe_users()
        return result

    def _subscribe_users(self):
        for mail_channel in self:
            mail_channel.write({
                'channel_partner_ids':
                [(4, pid) for pid in mail_channel.mapped('group_ids').mapped(
                    'users').mapped('partner_id').ids]
            })

    @api.multi
    def action_follow(self):
        self.ensure_one()
        channel_partner = self.mapped(
            'channel_last_seen_partner_ids').filtered(
                lambda cp: cp.partner_id == self.env.user.partner_id)
        if not channel_partner:
            return self.write({
                'channel_last_seen_partner_ids': [(0, 0, {
                    'partner_id':
                    self.env.user.partner_id.id
                })]
            })

    @api.multi
    def action_unfollow(self):
        partner_id = self.env.user.partner_id.id
        channel_info = self.channel_info('unsubscribe')[
            0]  # must be computed before leaving the channel (access rights)
        result = self.write({'channel_partner_ids': [(3, partner_id)]})
        self.env['bus.bus'].sendone(
            (self._cr.dbname, 'res.partner', partner_id), channel_info)
        if not self.email_send:
            notification = _(
                '<div class="o_mail_notification">left <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>'
            ) % (
                self.id,
                self.name,
            )
            # post 'channel left' message as root since the partner just unsubscribed from the channel
            self.sudo().message_post(body=notification,
                                     message_type="notification",
                                     subtype="mail.mt_comment",
                                     author_id=partner_id)
        return result

    @api.multi
    def _notification_group_recipients(self, message, recipients, done_ids,
                                       group_data):
        """ All recipients of a message on a channel are considered as partners.
        This means they will receive a minimal email, without a link to access
        in the backend. Mailing lists should indeed send minimal emails to avoid
        the noise. """
        for recipient in recipients:
            group_data['partner'] |= recipient
            done_ids.add(recipient.id)
        return super(Channel, self)._notification_group_recipients(
            message, recipients, done_ids, group_data)

    @api.multi
    def message_get_email_values(self, notif_mail=None):
        self.ensure_one()
        res = super(Channel,
                    self).message_get_email_values(notif_mail=notif_mail)
        headers = {}
        if res.get('headers'):
            try:
                headers.update(eval(res['headers']))
            except Exception:
                pass
        headers['Precedence'] = 'list'
        # avoid out-of-office replies from MS Exchange
        # http://blogs.technet.com/b/exchange/archive/2006/10/06/3395024.aspx
        headers['X-Auto-Response-Suppress'] = 'OOF'
        if self.alias_domain and self.alias_name:
            headers['List-Id'] = '%s.%s' % (self.alias_name, self.alias_domain)
            headers['List-Post'] = '<mailto:%s@%s>' % (self.alias_name,
                                                       self.alias_domain)
            # Avoid users thinking it was a personal message
            # X-Forge-To: will replace To: after SMTP envelope is determined by ir.mail.server
            list_to = '"%s" <%s@%s>' % (self.name, self.alias_name,
                                        self.alias_domain)
            headers['X-Forge-To'] = list_to
        res['headers'] = repr(headers)
        return res

    @api.multi
    def message_get_recipient_values(self,
                                     notif_message=None,
                                     recipient_ids=None):
        # real mailing list: multiple recipients (hidden by X-Forge-To)
        if self.alias_domain and self.alias_name:
            return {
                'email_to':
                ','.join(
                    formataddr((partner.name, partner.email)) for partner in
                    self.env['res.partner'].sudo().browse(recipient_ids)),
                'recipient_ids': [],
            }
        return super(Channel, self).message_get_recipient_values(
            notif_message=notif_message, recipient_ids=recipient_ids)

    @api.multi
    @api.returns('self', lambda value: value.id)
    def message_post(self,
                     body='',
                     subject=None,
                     message_type='notification',
                     subtype=None,
                     parent_id=False,
                     attachments=None,
                     content_subtype='html',
                     **kwargs):
        # auto pin 'direct_message' channel partner
        self.filtered(lambda channel: channel.channel_type == 'chat').mapped(
            'channel_last_seen_partner_ids').write({'is_pinned': True})
        # apply shortcode (text only) subsitution
        body = self.env['mail.shortcode'].apply_shortcode(
            body, shortcode_type='text')
        message = super(
            Channel,
            self.with_context(mail_create_nosubscribe=True)).message_post(
                body=body,
                subject=subject,
                message_type=message_type,
                subtype=subtype,
                parent_id=parent_id,
                attachments=attachments,
                content_subtype=content_subtype,
                **kwargs)
        return message

    #------------------------------------------------------
    # Instant Messaging API
    #------------------------------------------------------
    # A channel header should be broadcasted:
    #   - when adding user to channel (only to the new added partners)
    #   - when folding/minimizing a channel (only to the user making the action)
    # A message should be broadcasted:
    #   - when a message is posted on a channel (to the channel, using _notify() method)

    # Anonymous method
    @api.multi
    def _broadcast(self, partner_ids):
        """ Broadcast the current channel header to the given partner ids
            :param partner_ids : the partner to notify
        """
        notifications = self._channel_channel_notifications(partner_ids)
        self.env['bus.bus'].sendmany(notifications)

    @api.multi
    def _channel_channel_notifications(self, partner_ids):
        """ Generate the bus notifications of current channel for the given partner ids
            :param partner_ids : the partner to send the current channel header
            :returns list of bus notifications (tuple (bus_channe, message_content))
        """
        notifications = []
        for partner in self.env['res.partner'].browse(partner_ids):
            user_id = partner.user_ids and partner.user_ids[0] or False
            if user_id:
                for channel_info in self.sudo(user_id).channel_info():
                    notifications.append([(self._cr.dbname, 'res.partner',
                                           partner.id), channel_info])
        return notifications

    @api.multi
    def _notify(self, message):
        """ Broadcast the given message on the current channels.
            Send the message on the Bus Channel (uuid for public mail.channel, and partner private bus channel (the tuple)).
            A partner will receive only on message on its bus channel, even if this message belongs to multiple mail channel. Then 'channel_ids' field
            of the received message indicates on wich mail channel the message should be displayed.
            :param : mail.message to broadcast
        """
        message.ensure_one()
        notifications = self._channel_message_notifications(message)
        self.env['bus.bus'].sendmany(notifications)

    @api.multi
    def _channel_message_notifications(self, message):
        """ Generate the bus notifications for the given message
            :param message : the mail.message to sent
            :returns list of bus notifications (tuple (bus_channe, message_content))
        """
        message_values = message.message_format()[0]
        notifications = []
        for channel in self:
            notifications.append([(self._cr.dbname, 'mail.channel',
                                   channel.id),
                                  dict(message_values)])
            # add uuid to allow anonymous to listen
            if channel.public == 'public':
                notifications.append([channel.uuid, dict(message_values)])
        return notifications

    @api.multi
    def channel_info(self, extra_info=False):
        """ Get the informations header for the current channels
            :returns a list of channels values
            :rtype : list(dict)
        """
        channel_infos = []
        partner_channels = self.env['mail.channel.partner']
        # find the channel partner state, if logged user
        if self.env.user and self.env.user.partner_id:
            partner_channels = self.env['mail.channel.partner'].search([
                ('partner_id', '=', self.env.user.partner_id.id),
                ('channel_id', 'in', self.ids)
            ])
        # for each channel, build the information header and include the logged partner information
        for channel in self:
            info = {
                'id': channel.id,
                'name': channel.name,
                'uuid': channel.uuid,
                'state': 'open',
                'is_minimized': False,
                'channel_type': channel.channel_type,
                'public': channel.public,
                'mass_mailing': channel.email_send,
            }
            if extra_info:
                info['info'] = extra_info
            # add the partner for 'direct mesage' channel
            if channel.channel_type == 'chat':
                info['direct_partner'] = (channel.sudo().with_context(
                    active_test=False).channel_partner_ids.filtered(
                        lambda p: p.id != self.env.user.partner_id.id).read(
                            ['id', 'name', 'im_status']))
            # add user session state, if available and if user is logged
            if partner_channels.ids:
                partner_channel = partner_channels.filtered(
                    lambda c: channel.id == c.channel_id.id)
                if len(partner_channel) >= 1:
                    partner_channel = partner_channel[0]
                    info['state'] = partner_channel.fold_state or 'open'
                    info['is_minimized'] = partner_channel.is_minimized
                    info[
                        'seen_message_id'] = partner_channel.seen_message_id.id
                # add needaction and unread counter, since the user is logged
                info[
                    'message_needaction_counter'] = channel.message_needaction_counter
                info['message_unread_counter'] = channel.message_unread_counter
            channel_infos.append(info)
        return channel_infos

    @api.multi
    def channel_fetch_message(self, last_id=False, limit=20):
        """ Return message values of the current channel.
            :param last_id : last message id to start the research
            :param limit : maximum number of messages to fetch
            :returns list of messages values
            :rtype : list(dict)
        """
        self.ensure_one()
        domain = [("channel_ids", "in", self.ids)]
        if last_id:
            domain.append(("id", "<", last_id))
        return self.env['mail.message'].message_fetch(domain=domain,
                                                      limit=limit)

    # User methods
    @api.model
    def channel_get(self, partners_to, pin=True):
        """ Get the canonical private channel between some partners, create it if needed.
            To reuse an old channel (conversation), this one must be private, and contains
            only the given partners.
            :param partners_to : list of res.partner ids to add to the conversation
            :param pin : True if getting the channel should pin it for the current user
            :returns a channel header, or False if the users_to was False
            :rtype : dict
        """
        if partners_to:
            partners_to.append(self.env.user.partner_id.id)
            # determine type according to the number of partner in the channel
            self.env.cr.execute(
                """
                SELECT P.channel_id as channel_id
                FROM mail_channel C, mail_channel_partner P
                WHERE P.channel_id = C.id
                    AND C.public LIKE 'private'
                    AND P.partner_id IN %s
                    AND channel_type LIKE 'chat'
                GROUP BY P.channel_id
                HAVING COUNT(P.partner_id) = %s
            """, (
                    tuple(partners_to),
                    len(partners_to),
                ))
            result = self.env.cr.dictfetchall()
            if result:
                # get the existing channel between the given partners
                channel = self.browse(result[0].get('channel_id'))
                # pin up the channel for the current partner
                if pin:
                    self.env['mail.channel.partner'].search([
                        ('partner_id', '=', self.env.user.partner_id.id),
                        ('channel_id', '=', channel.id)
                    ]).write({'is_pinned': True})
            else:
                # create a new one
                channel = self.create({
                    'channel_partner_ids':
                    [(4, partner_id) for partner_id in partners_to],
                    'public':
                    'private',
                    'channel_type':
                    'chat',
                    'email_send':
                    False,
                    'name':
                    ', '.join(self.env['res.partner'].sudo().browse(
                        partners_to).mapped('name')),
                })
                # broadcast the channel header to the other partner (not me)
                channel._broadcast(partners_to)
            return channel.channel_info()[0]
        return False

    @api.model
    def channel_get_and_minimize(self, partners_to):
        channel = self.channel_get(partners_to)
        if channel:
            self.channel_minimize(channel['uuid'])
        return channel

    @api.model
    def channel_fold(self, uuid, state=None):
        """ Update the fold_state of the given session. In order to syncronize web browser
            tabs, the change will be broadcast to himself (the current user channel).
            Note: the user need to be logged
            :param state : the new status of the session for the current user.
        """
        domain = [('partner_id', '=', self.env.user.partner_id.id),
                  ('channel_id.uuid', '=', uuid)]
        for session_state in self.env['mail.channel.partner'].search(domain):
            if not state:
                state = session_state.fold_state
                if session_state.fold_state == 'open':
                    state = 'folded'
                else:
                    state = 'open'
            session_state.write({
                'fold_state': state,
                'is_minimized': bool(state != 'closed'),
            })
            self.env['bus.bus'].sendone(
                (self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
                session_state.channel_id.channel_info()[0])

    @api.model
    def channel_minimize(self, uuid, minimized=True):
        values = {
            'fold_state': minimized and 'open' or 'closed',
            'is_minimized': minimized
        }
        domain = [('partner_id', '=', self.env.user.partner_id.id),
                  ('channel_id.uuid', '=', uuid)]
        channel_partners = self.env['mail.channel.partner'].search(domain)
        channel_partners.write(values)
        self.env['bus.bus'].sendone(
            (self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
            channel_partners.channel_id.channel_info()[0])

    @api.model
    def channel_pin(self, uuid, pinned=False):
        # add the person in the channel, and pin it (or unpin it)
        channel = self.search([('uuid', '=', uuid)])
        channel_partners = self.env['mail.channel.partner'].search([
            ('partner_id', '=', self.env.user.partner_id.id),
            ('channel_id', '=', channel.id)
        ])
        if not pinned:
            self.env['bus.bus'].sendone(
                (self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
                channel.channel_info('unsubscribe')[0])
        if channel_partners:
            channel_partners.write({'is_pinned': pinned})

    @api.multi
    def channel_seen(self):
        self.ensure_one()
        if self.channel_message_ids.ids:
            last_message_id = self.channel_message_ids.ids[
                0]  # zero is the index of the last message
            self.env['mail.channel.partner'].search([
                ('channel_id', 'in', self.ids),
                ('partner_id', '=', self.env.user.partner_id.id)
            ]).write({'seen_message_id': last_message_id})
            self.env['bus.bus'].sendone(
                (self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
                {
                    'info': 'channel_seen',
                    'id': self.id,
                    'last_message_id': last_message_id
                })
            return last_message_id

    @api.multi
    def channel_invite(self, partner_ids):
        """ Add the given partner_ids to the current channels and broadcast the channel header to them.
            :param partner_ids : list of partner id to add
        """
        partners = self.env['res.partner'].browse(partner_ids)
        # add the partner
        for channel in self:
            partners_to_add = partners - channel.channel_partner_ids
            channel.write({
                'channel_last_seen_partner_ids': [(0, 0, {
                    'partner_id': partner_id
                }) for partner_id in partners_to_add.ids]
            })
            for partner in partners_to_add:
                notification = _(
                    '<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>'
                ) % (
                    self.id,
                    self.name,
                )
                self.message_post(body=notification,
                                  message_type="notification",
                                  subtype="mail.mt_comment",
                                  author_id=partner.id)

        # broadcast the channel header to the added partner
        self._broadcast(partner_ids)

    #------------------------------------------------------
    # Instant Messaging View Specific (Slack Client Action)
    #------------------------------------------------------
    @api.model
    def get_init_notifications(self):
        """ Get unread messages and old messages received less than AWAY_TIMER
            ago of minimized channel ONLY. This aims to set the minimized channel
            when refreshing the page.
            Note : the user need to be logged
        """
        # get current user's minimzed channel
        minimized_channels = self.env['mail.channel.partner'].search([
            ('is_minimized', '=', True),
            ('partner_id', '=', self.env.user.partner_id.id)
        ]).mapped('channel_id')

        # get the message since the AWAY_TIMER
        threshold = datetime.datetime.now() - datetime.timedelta(
            seconds=AWAY_TIMER)
        threshold = threshold.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        domain = [('channel_ids', 'in', minimized_channels.ids),
                  ('create_date', '>', threshold)]

        # get the message since the last poll of the user
        presence = self.env['bus.presence'].search(
            [('user_id', '=', self._uid)], limit=1)
        if presence:
            domain.append(('create_date', '>', presence.last_poll))

        # do the message search
        message_values = self.env['mail.message'].message_fetch(domain=domain)

        # create the notifications (channel infos first, then messages)
        notifications = []
        for channel_info in minimized_channels.channel_info():
            notifications.append([(self._cr.dbname, 'res.partner',
                                   self.env.user.partner_id.id), channel_info])
        for message_value in message_values:
            for channel_id in message_value['channel_ids']:
                if channel_id in minimized_channels.ids:
                    message_value['channel_ids'] = [channel_id]
                    notifications.append([(self._cr.dbname, 'mail.channel',
                                           channel_id),
                                          dict(message_value)])
        return notifications

    @api.model
    def channel_fetch_slot(self):
        """ Return the channels of the user grouped by 'slot' (channel, direct_message or private_group), and
            the mapping between partner_id/channel_id for direct_message channels.
            :returns dict : the grouped channels and the mapping
        """
        values = {}
        my_partner_id = self.env.user.partner_id.id
        pinned_channels = self.env['mail.channel.partner'].search([
            ('partner_id', '=', my_partner_id), ('is_pinned', '=', True)
        ]).mapped('channel_id')

        # get the group/public channels
        values['channel_channel'] = self.search([
            ('channel_type', '=', 'channel'),
            ('public', 'in', ['public', 'groups']),
            ('channel_partner_ids', 'in', [my_partner_id])
        ]).channel_info()

        # get the pinned 'direct message' channel
        direct_message_channels = self.search([('channel_type', '=', 'chat'),
                                               ('id', 'in',
                                                pinned_channels.ids)])
        values[
            'channel_direct_message'] = direct_message_channels.channel_info()

        # get the private group
        values['channel_private_group'] = self.search([
            ('channel_type', '=', 'channel'), ('public', '=', 'private'),
            ('channel_partner_ids', 'in', [my_partner_id])
        ]).channel_info()
        return values

    @api.model
    def channel_search_to_join(self, name=None, domain=None):
        """ Return the channel info of the channel the current partner can join
            :param name : the name of the researched channels
            :param domain : the base domain of the research
            :returns dict : channel dict
        """
        if not domain:
            domain = []
        domain = expression.AND([[('channel_type', '=', 'channel')],
                                 [('channel_partner_ids', 'not in',
                                   [self.env.user.partner_id.id])],
                                 [('public', '!=', 'private')], domain])
        if name:
            domain = expression.AND(
                [domain, [('name', 'ilike', '%' + name + '%')]])
        return self.search(domain).read(
            ['name', 'public', 'uuid', 'channel_type'])

    @api.multi
    def channel_join_and_get_info(self):
        self.ensure_one()
        if self.channel_type == 'channel' and not self.email_send:
            notification = _(
                '<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>'
            ) % (
                self.id,
                self.name,
            )
            self.message_post(body=notification,
                              message_type="notification",
                              subtype="mail.mt_comment")
        self.action_follow()

        channel_info = self.channel_info()[0]
        self.env['bus.bus'].sendone(
            (self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
            channel_info)
        return channel_info

    @api.model
    def channel_create(self, name, privacy='public'):
        """ Create a channel and add the current partner, broadcast it (to make the user directly
            listen to it when polling)
            :param name : the name of the channel to create
            :param privacy : privacy of the channel. Should be 'public' or 'private'.
            :return dict : channel header
        """
        # create the channel
        new_channel = self.create({
            'name':
            name,
            'public':
            privacy,
            'email_send':
            False,
            'channel_partner_ids': [(4, self.env.user.partner_id.id)]
        })
        channel_info = new_channel.channel_info('creation')[0]
        notification = _(
            '<div class="o_mail_notification">created <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>'
        ) % (
            new_channel.id,
            new_channel.name,
        )
        new_channel.message_post(body=notification,
                                 message_type="notification",
                                 subtype="mail.mt_comment")
        self.env['bus.bus'].sendone(
            (self._cr.dbname, 'res.partner', self.env.user.partner_id.id),
            channel_info)
        return channel_info

    @api.model
    def get_mention_suggestions(self, search, limit=8):
        """ Return 'limit'-first channels' id, name and public fields such that the name matches a
            'search' string. Exclude channels of type chat (DM), and private channels the current
            user isn't registered to. """
        domain = expression.AND([[('name', 'ilike', search)],
                                 [('channel_type', '=', 'channel')],
                                 expression.OR(
                                     [[('public', '!=', 'private')],
                                      [('channel_partner_ids', 'in',
                                        [self.env.user.partner_id.id])]])])
        return self.search_read(domain, ['id', 'name', 'public'], limit=limit)

    @api.model
    def channel_fetch_listeners(self, uuid):
        """ Return the id, name and email of partners listening to the given channel """
        self._cr.execute(
            """
            SELECT P.id, P.name, P.email
            FROM mail_channel_partner CP
                INNER JOIN res_partner P ON CP.partner_id = P.id
                INNER JOIN mail_channel C ON CP.channel_id = C.id
            WHERE C.uuid = %s""", (uuid, ))
        return self._cr.dictfetchall()

    @api.multi
    def channel_fetch_preview(self):
        """ Return the last message of the given channels """
        self._cr.execute(
            """
            SELECT mail_channel_id AS id, MAX(mail_message_id) AS message_id
            FROM mail_message_mail_channel_rel
            WHERE mail_channel_id IN %s
            GROUP BY mail_channel_id
            """, (tuple(self.ids), ))
        channels_preview = dict(
            (r['message_id'], r) for r in self._cr.dictfetchall())
        last_messages = self.env['mail.message'].browse(
            channels_preview.keys()).message_format()
        for message in last_messages:
            channel = channels_preview[message['id']]
            del (channel['message_id'])
            channel['last_message'] = message
        return channels_preview.values()
Example #30
0
class ResPartner(models.Model):
    _inherit = 'res.partner'

    dom = "['|',                                      " \
          "  ('on_contact' ,'!=', is_company        )," \
          "  '|',                                     " \
          "   '&',                                    " \
          "    ('on_company' , '=', is_company      )," \
          "    ('on_company' , '=', state=='perjur' )," \
          "   '&',                                    " \
          "    ('on_merchant', '=', state=='pernat' )," \
          "    ('on_merchant', '=', is_company      )]"

    fiscal_id_type = fields.Many2one(
        'res.partner.idtype',
        string=u'Tipo de documento',
        domain=dom,
    )
    fiscal_id = fields.Char(
        string=u'Nro Documento',
        # compute='validateformatcopy',
    )
    fiscal_id_doc = fields.Binary(
        string=u'Adjunto Escaneado',
        help="Sube tu documento adjunto "
             "Preferiblemente en formato PDF"
             "Esto te permite, ahorrar espacio en disco "
             "y concatenar varios documentos"
    )

    @api.one
    @api.onchange(
        'fiscal_id_type',
        'fiscal_id',
        'is_company',
    )
    def validateformatcopy(self):
        # CASE: Current ID Type is not applicable on Merchant
        if self.is_company and self.state == 'pernat':
            if not self.fiscal_id_type.on_merchant:
                # Get the first valid ID type (remember: ordered by sequence)
                self.fiscal_id_type = self.env['res.partner.idtype'].search(
                    [('on_merchant', '=', True)], limit=1).id
                self.fiscal_id = None  # Reset ID value
        # CASE: Current ID Type is not applicable on Company
        if self.is_company and self.state == 'perjur':
            if not self.fiscal_id_type.on_company:
                # Get the first valid ID type (remember: ordered by sequence)
                self.fiscal_id_type = self.env['res.partner.idtype'].search(
                    [('on_company', '=', True)], limit=1).id
                self.fiscal_id = None  # Reset ID value
        # CASE: Current ID Type is not applicable on contact
        if not self.is_company:
            if not self.fiscal_id_type.on_contact:
                # Get the first valid ID type (remember: ordered by sequence)
                self.fiscal_id_type = self.env['res.partner.idtype'].search(
                    [('on_contact', '=', True)], limit=1).id
                self.fiscal_id = None  # Reset ID value
        # If everything is fine, call subclasses
        if self.fiscal_id_type and self.fiscal_id:
            # Function for String Operations
            res = self._validateandformatid()
            if res['output_type'] and res['output_id']:
                self.fiscal_id_type = res['output_type']
                self.fiscal_id = res['output_id']
            # Procedure for Copying
            self._copyid()

    def _validateandformatid(self):
        """
        Hook method to be inherited for custom validation methods.
        :param input_type: the value of the field fiscal_id_type (id); passed
        on by onchange decorator
        :param input_id: the value of the field fiscal_id (string); passed on
        by onchange decorator
        :return: must return a dict with validated and formatted values

        Hint:
        you might not alter the output_type unless you might want to build
        some kind of fiscal_id_type recognition
        based on the input pattern into your hook method. CO###.###.###-#
        CO-VAT (NIT) for example.

        Find below a suggested basic outline.

        """
        return {'output_type': self.fiscal_id_type, 'output_id': self.fiscal_id}
        """
        f_type     = self.fiscal_id_type
        f_id       = self.fiscal_id
        is_company = self.is_company

        def default():
            return {'output_type': f_type, 'output_id': f_id}

        return {
            # Define your cases
            # The index to match is self.fiscal_id_type.code
            # Note: You can change this index below.
            # Example assignation using two functions
            # {'output_type': func_type1(), 'output_id': funct_id1()}
            'CODE1': { "put your assignation here" },
            'CODE2': { "put your assignation here" },
        }.get(self.fiscal_id_type.code, default())
        """

    def _copyid(self):
        """
        Hook Method to be inherited for custom copy methods based on the
        document type (id)
        Example Use Case: Copy some local VAT number into the VAT-Field in
        it's international format for compatibility.

        :return: It is a Procedure and therefore has no return value.

        Find below a suggested basic outline.

        """
        """