class base_module_update(models.TransientModel): _name = "base.module.update" _description = "Update Module" updated = fields.Integer('Number of modules updated', readonly=True) added = fields.Integer('Number of modules added', readonly=True) state = fields.Selection([('init', 'init'), ('done', 'done')], 'Status', readonly=True, default='init') @api.one def update_module(self): self.updated, self.added = self.env['ir.module.module'].update_list() self.state = 'done' return False @api.multi def action_module_open(self): res = { 'domain': str([]), 'name': 'Modules', 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'ir.module.module', 'view_id': False, 'type': 'ir.actions.act_window', } return res
class HrDepartment(models.Model): _inherit = 'hr.department' new_applicant_count = fields.Integer( compute='_compute_new_applicant_count', string='New Applicant') new_hired_employee = fields.Integer(compute='_compute_recruitment_stats', string='New Hired Employee') expected_employee = fields.Integer(compute='_compute_recruitment_stats', string='Expected Employee') @api.multi def _compute_new_applicant_count(self): applicant_data = self.env['hr.applicant'].read_group( [('department_id', 'in', self.ids), ('stage_id.sequence', '<=', '1')], ['department_id'], ['department_id']) result = dict((data['department_id'][0], data['department_id_count']) for data in applicant_data) for department in self: department.new_applicant_count = result.get(department.id, 0) @api.multi def _compute_recruitment_stats(self): job_data = self.env['hr.job'].read_group( [('department_id', 'in', self.ids)], ['no_of_hired_employee', 'no_of_recruitment', 'department_id'], ['department_id']) new_emp = dict((data['department_id'][0], data['no_of_hired_employee']) for data in job_data) expected_emp = dict( (data['department_id'][0], data['no_of_recruitment']) for data in job_data) for department in self: department.new_hired_employee = new_emp.get(department.id, 0) department.expected_employee = expected_emp.get(department.id, 0)
class AccountAssetCategory(models.Model): _name = 'account.asset.category' _description = 'Asset category' active = fields.Boolean(default=True) name = fields.Char(required=True, index=True, string="Asset Type") account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic Account') account_asset_id = fields.Many2one('account.account', string='Asset Account', required=True, domain=[('internal_type','=','other'), ('deprecated', '=', False)]) account_income_recognition_id = fields.Many2one('account.account', string='Recognition Income Account', domain=[('internal_type','=','other'), ('deprecated', '=', False)], oldname='account_expense_depreciation_id') account_depreciation_id = fields.Many2one('account.account', string='Depreciation Account', required=True, domain=[('internal_type','=','other'), ('deprecated', '=', False)]) journal_id = fields.Many2one('account.journal', string='Journal', required=True) company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env['res.company']._company_default_get('account.asset.category')) method = fields.Selection([('linear', 'Linear'), ('degressive', 'Degressive')], string='Computation Method', required=True, default='linear', help="Choose the method to use to compute the amount of depreciation lines.\n" " * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n" " * Degressive: Calculated on basis of: Residual Value * Degressive Factor") method_number = fields.Integer(string='Number of Depreciations', default=5, help="The number of depreciations needed to depreciate your asset") method_period = fields.Integer(string='Period Length', default=1, help="State here the time between 2 depreciations, in months", required=True) method_progress_factor = fields.Float('Degressive Factor', default=0.3) method_time = fields.Selection([('number', 'Number of Depreciations'), ('end', 'Ending Date')], string='Time Method', required=True, default='number', help="Choose the method to use to compute the dates and number of depreciation lines.\n" " * Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" " * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond.") method_end = fields.Date('Ending date') prorata = fields.Boolean(string='Prorata Temporis', help='Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first of January') open_asset = fields.Boolean(string='Post Journal Entries', help="Check this if you want to automatically confirm the assets of this category when created by invoices.") type = fields.Selection([('sale', 'Sale: Revenue Recognition'), ('purchase', 'Purchase: Asset')], required=True, index=True, default='purchase') @api.onchange('type') def onchange_type(self): if self.type == 'sale': self.prorata = True self.method_period = 1 else: self.method_period = 12
class SaasClient(models.AbstractModel): _name = 'saas_base.client' users_len = fields.Integer('Count users', readonly=True) max_users = fields.Char('Max users allowed', readonly=True) file_storage = fields.Integer('File storage (MB)', readonly=True) db_storage = fields.Integer('DB storage (MB)', readonly=True) total_storage_limit = fields.Integer('Total storage limit (MB)', readonly=True, default=0) expiration_datetime = fields.Datetime('Expiration', track_visibility='onchange') trial = fields.Boolean('Trial', help='indication of trial clients', default=False, readonly=True)
class HrEquipmentCategory(models.Model): _name = 'hr.equipment.category' _inherits = {"mail.alias": "alias_id"} _inherit = ['mail.thread'] _description = 'Asset Category' @api.one @api.depends('equipment_ids') def _compute_fold(self): self.fold = False if self.equipment_count else True name = fields.Char('Category Name', required=True, translate=True) user_id = fields.Many2one('res.users', 'Responsible', track_visibility='onchange', default=lambda self: self.env.uid) color = fields.Integer('Color Index') note = fields.Text('Comments', translate=True) equipment_ids = fields.One2many('hr.equipment', 'category_id', string='Equipments', copy=False) equipment_count = fields.Integer(string="Equipment", compute='_compute_equipment_count') maintenance_ids = fields.One2many('hr.equipment.request', 'category_id', copy=False) maintenance_count = fields.Integer(string="Maintenance", compute='_compute_maintenance_count') alias_id = fields.Many2one( 'mail.alias', 'Alias', ondelete='cascade', required=True, help="Email alias for this equipment category. New emails will automatically " "create new maintenance request for this equipment category.") fold = fields.Boolean(string='Folded in Maintenance Pipe', compute='_compute_fold', store=True) @api.multi def _compute_equipment_count(self): equipment_data = self.env['hr.equipment'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id']) mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in equipment_data]) for category in self: category.equipment_count = mapped_data.get(category.id, 0) @api.multi def _compute_maintenance_count(self): maintenance_data = self.env['hr.equipment.request'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id']) mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in maintenance_data]) for category in self: category.maintenance_count = mapped_data.get(category.id, 0) @api.model def create(self, vals): self = self.with_context(alias_model_name='hr.equipment.request', alias_parent_model_name=self._name) category_id = super(HrEquipmentCategory, self).create(vals) category_id.alias_id.write({'alias_parent_thread_id': category_id.id, 'alias_defaults': {'category_id': category_id.id}}) return category_id @api.multi def unlink(self): for category in self: if category.equipment_ids or category.maintenance_ids: raise UserError(_("You cannot delete an equipment category containing equipments or maintenance requests.")) res = super(HrEquipmentCategory, self).unlink() return res
class DateRangeGenerator(models.TransientModel): _name = 'date.range.generator' @api.model def _default_company(self): return self.env['res.company']._company_default_get('date.range') name_prefix = fields.Char('Range name prefix', required=True) date_start = fields.Date(strint='Start date', required=True) type_id = fields.Many2one(comodel_name='date.range.type', string='Type', required=True, ondelete='cascade') company_id = fields.Many2one(comodel_name='res.company', string='Company', default=_default_company) unit_of_time = fields.Selection([(YEARLY, 'years'), (MONTHLY, 'months'), (WEEKLY, 'weeks'), (DAILY, 'days')], required=True) duration_count = fields.Integer('Duration', required=True) count = fields.Integer(string="Number of ranges to generate", required=True) @api.multi def _compute_date_ranges(self): self.ensure_one() vals = rrule(freq=self.unit_of_time, interval=self.duration_count, dtstart=fields.Date.from_string(self.date_start), count=self.count + 1) vals = list(vals) date_ranges = [] for idx, dt_start in enumerate(vals[:-1]): date_start = fields.Date.to_string(dt_start.date()) dt_end = vals[idx + 1].date() - relativedelta(days=1) date_end = fields.Date.to_string(dt_end) date_ranges.append({ 'name': '%s-%d' % (self.name_prefix, idx + 1), 'date_start': date_start, 'date_end': date_end, 'type_id': self.type_id.id, 'company_id': self.company_id.id }) return date_ranges @api.multi def action_apply(self): date_ranges = self._compute_date_ranges() if date_ranges: for dr in date_ranges: self.env['date.range'].create(dr) return self.env['ir.actions.act_window'].for_xml_id( module='date_range', xml_id='date_range_action')
class ImLivechatChannelRule(models.Model): """ Channel Rules Rules defining access to the channel (countries, and url matching). It also provide the 'auto pop' option to open automatically the conversation. """ _name = 'im_livechat.channel.rule' _description = 'Channel Rules' _order = 'sequence asc' regex_url = fields.Char('URL Regex', help="Regular expression identifying the web page on which the rules will be applied.") action = fields.Selection([('display_button', 'Display the button'), ('auto_popup', 'Auto popup'), ('hide_button', 'Hide the button')], string='Action', required=True, default='display_button', help="* Select 'Display the button' to simply display the chat button on the pages.\n"\ "* Select 'Auto popup' for to display the button, and automatically open the conversation window.\n"\ "* Select 'Hide the button' to hide the chat button on the pages.") auto_popup_timer = fields.Integer('Auto popup timer', default=0, help="Delay (in seconds) to automatically open the converssation window. Note : the selected action must be 'Auto popup', otherwise this parameter will not be take into account.") channel_id = fields.Many2one('im_livechat.channel', 'Channel', help="The channel of the rule") country_ids = fields.Many2many('res.country', 'im_livechat_channel_country_rel', 'channel_id', 'country_id', 'Country', help="The actual rule will match only for this country. So if you set select 'Belgium' and 'France' and you set the action to 'Hide Buttun', this 2 country will not be see the support button for the specified URL. This feature requires GeoIP installed on your server.") sequence = fields.Integer('Matching order', default=10, help="Given the order to find a matching rule. If 2 rules are matching for the given url/country, the one with the lowest sequence will be chosen.") def match_rule(self, channel_id, url, country_id=False): """ determine if a rule of the given channel match with the given url :param channel_id : the identifier of the channel_id :param url : the url to match with a rule :param country_id : the identifier of the country :returns the rule that match the given condition. False otherwise. :rtype : im_livechat.channel.rule """ def _match(rules): for rule in rules: if re.search(rule.regex_url, url): return rule return False # first, search the country specific rules (the first match is returned) if country_id: # don't include the country in the research if geoIP is not installed domain = [('country_ids', 'in', [country_id]), ('channel_id', '=', channel_id)] rule = _match(self.search(domain)) if rule: return rule # second, fallback on the rules without country domain = [('country_ids', '=', False), ('channel_id', '=', channel_id)] return _match(self.search(domain))
class CrmActivity(models.Model): ''' CrmActivity is a model introduced in eCore v9 that models activities performed in CRM, like phonecalls, sending emails, making demonstrations, ... Users are able to configure their custom activities. Each activity has up to three next activities. This allows to model light custom workflows. This way sales manager can configure their crm workflow that salepersons will use in their daily job. CrmActivity inherits from mail.message.subtype. This allows users to follow some activities through subtypes. Each activity will generate messages with the matching subtypes, allowing reporting and statistics computation based on mail.message.subtype model. ''' _name = 'crm.activity' _description = 'CRM Activity' _inherits = {'mail.message.subtype': 'subtype_id'} _rec_name = 'name' _order = "sequence" days = fields.Integer( 'Number of days', default=0, help= 'Number of days before executing the action, allowing you to plan the date of the action.' ) sequence = fields.Integer('Sequence', default=0) team_id = fields.Many2one('crm.team', string='Sales Team') subtype_id = fields.Many2one('mail.message.subtype', string='Message Subtype', required=True, ondelete='cascade') activity_1_id = fields.Many2one('crm.activity', string="Next Activity 1") activity_2_id = fields.Many2one('crm.activity', string="Next Activity 2") activity_3_id = fields.Many2one('crm.activity', string="Next Activity 3") @api.model def create(self, values): ''' Override to set the res_model of inherited subtype to crm.lead. This cannot be achieved using a default on res_model field because of the inherits. Indeed a new field would be created. However the field on the subtype would still exist. Being void, the subtype will be present for every model in eCore. That's quite an issue. ''' if not values.get( 'res_model') and 'default_res_model' not in self._context: values['res_model'] = 'crm.lead' if 'internal' not in values and 'default_internal' not in self._context: values['internal'] = True return super(CrmActivity, self).create(values)
class PriceRule(models.Model): _name = "delivery.price.rule" _description = "Delivery Price Rules" _order = 'sequence, list_price' @api.depends('variable', 'operator', 'max_value', 'list_base_price', 'list_price', 'variable_factor') def _get_name(self): for rule in self: name = 'if %s %s %s then' % (rule.variable, rule.operator, rule.max_value) if rule.list_base_price and not rule.list_price: name = '%s fixed price %s' % (name, rule.list_base_price) elif rule.list_price and not rule.list_base_price: name = '%s %s times %s' % (name, rule.list_price, rule.variable_factor) else: name = '%s fixed price %s and %s times %s Extra' % (name, rule.list_base_price, rule.list_price, rule.variable_factor) rule.name = name name = fields.Char(compute='_get_name') sequence = fields.Integer(required=True, help="Gives the sequence order when calculating delivery carrier.", default=10) carrier_id = fields.Many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade') variable = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable', required=True, default='weight') operator = fields.Selection([('==', '='), ('<=', '<='), ('<', '<'), ('>=', '>='), ('>', '>')], 'Operator', required=True, default='<=') max_value = fields.Float('Maximum Value', required=True) variable_factor = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable Factor', required=True, default='weight') list_base_price = fields.Float(string='Sale Base Price', digits=dp.get_precision('Product Price'), required=True, default=0.0) list_price = fields.Float('Sale Price', digits=dp.get_precision('Product Price'), required=True, default=0.0) standard_price = fields.Float('Cost Price', digits=dp.get_precision('Product Price'), required=True, default=0.0)
class account_bank_statement_line(models.Model): _inherit = "account.bank.statement.line" mercury_card_number = fields.Char( string='Card Number', help='The last 4 numbers of the card used to pay') mercury_prefixed_card_number = fields.Char( string='Card Number', compute='_compute_prefixed_card_number', help='The card number used for the payment.') mercury_card_brand = fields.Char( string='Card Brand', help='The brand of the payment card (e.g. Visa, AMEX, ...)') mercury_card_owner_name = fields.Char(string='Card Owner Name', help='The name of the card owner') mercury_ref_no = fields.Char( string='Mercury reference number', help='Payment reference number from Mercury Pay') mercury_record_no = fields.Char( string='Mercury record number', help='Payment record number from Mercury Pay') mercury_invoice_no = fields.Integer(string='Mercury invoice number', help='Invoice number from Mercury Pay') @api.one def _compute_prefixed_card_number(self): if self.mercury_card_number: self.mercury_prefixed_card_number = "********" + self.mercury_card_number else: self.mercury_prefixed_card_number = ""
class EmbeddedSlide(models.Model): """ Embedding in third party websites. Track view count, generate statistics. """ _name = 'slide.embed' _description = 'Embedded Slides View Counter' _rec_name = 'slide_id' slide_id = fields.Many2one('slide.slide', string="Presentation", required=True, select=1) url = fields.Char('Third Party Website URL', required=True) count_views = fields.Integer('# Views', default=1) def add_embed_url(self, slide_id, url): schema = urlparse(url) baseurl = schema.netloc embeds = self.search([('url', '=', baseurl), ('slide_id', '=', int(slide_id))], limit=1) if embeds: embeds.count_views += 1 else: embeds = self.create({ 'slide_id': slide_id, 'url': baseurl, }) return embeds.count_views
class RecruitmentStage(models.Model): _name = "hr.recruitment.stage" _description = "Stage of Recruitment" _order = 'sequence' name = fields.Char("Stage name", required=True, translate=True) sequence = fields.Integer( "Sequence", default=1, help="Gives the sequence order when displaying a list of stages.") job_ids = fields.Many2many( 'hr.job', 'job_stage_rel', 'stage_id', 'job_id', string='Job Stages', default=lambda self: [(4, self._context['default_job_id'])] if self._context.get('default_job_id') else None) requirements = fields.Text("Requirements") template_id = fields.Many2one( 'mail.template', "Use template", help= "If set, a message is posted on the applicant using the template when the applicant is set to the stage." ) fold = fields.Boolean( "Folded in Recruitment Pipe", help= "This stage is folded in the kanban view when there are no records in that stage to display." )
class product_product(models.Model): _inherit = 'product.product' attachment_count = fields.Integer(compute='_compute_attachment_count', string="File") @api.multi def _compute_attachment_count(self): IrAttachment = self.env['ir.attachment'] for product in self: product.attachment_count = IrAttachment.search_count([ ('res_model', '=', product._name), ('res_id', 'in', product.ids) ]) @api.multi def action_open_attachments(self): self.ensure_one() return { 'name': _('Digital Attachments'), 'domain': [('res_model', '=', self._name), ('res_id', '=', self.id)], 'res_model': 'ir.attachment', 'type': 'ir.actions.act_window', 'view_mode': 'kanban,form', 'view_type': 'form', 'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, self.id), }
class Tags(models.Model): _name = "forum.tag" _description = "Forum Tag" _inherit = ['website.seo.metadata'] name = fields.Char('Name', required=True) create_uid = fields.Many2one('res.users', string='Created by', readonly=True) forum_id = fields.Many2one('forum.forum', string='Forum', required=True) post_ids = fields.Many2many('forum.post', 'forum_tag_rel', 'forum_tag_id', 'forum_id', string='Posts') posts_count = fields.Integer('Number of Posts', compute='_get_posts_count', store=True) _sql_constraints = [ ('name_uniq', 'unique (name, forum_id)', "Tag name already exists !"), ] @api.multi @api.depends("post_ids.tag_ids") def _get_posts_count(self): for tag in self: tag.posts_count = len(tag.post_ids)
class ImLivechatReportChannel(models.Model): """ Livechat Support Report on the Channels """ _name = "im_livechat.report.channel" _description = "Livechat Support Report" _order = 'start_date, technical_name' _auto = False uuid = fields.Char('UUID', readonly=True) channel_id = fields.Many2one('mail.channel', 'Conversation', readonly=True) channel_name = fields.Char('Channel Name', readonly=True) technical_name = fields.Char('Code', readonly=True) livechat_channel_id = fields.Many2one('im_livechat.channel', 'Channel', readonly=True) start_date = fields.Datetime('Start Date of session', readonly=True, help="Start date of the conversation") start_date_hour = fields.Char('Hour of start Date of session', readonly=True) duration = fields.Float('Average duration', digits=(16, 2), readonly=True, group_operator="avg", help="Duration of the conversation (in seconds)") nbr_speaker = fields.Integer('# of speakers', readonly=True, group_operator="avg", help="Number of different speakers") nbr_message = fields.Integer('Average message', readonly=True, group_operator="avg", help="Number of message in the conversation") partner_id = fields.Many2one('res.partner', 'Opertor', readonly=True) def init(self, cr): # Note : start_date_hour must be remove when the read_group will allow grouping on the hour of a datetime. Don't forget to change the view ! tools.drop_view_if_exists(cr, 'im_livechat_report_channel') cr.execute(""" CREATE OR REPLACE VIEW im_livechat_report_channel AS ( SELECT C.id as id, C.uuid as uuid, C.id as channel_id, C.name as channel_name, CONCAT(L.name, ' / ', C.id) as technical_name, C.livechat_channel_id as livechat_channel_id, C.create_date as start_date, to_char(date_trunc('hour', C.create_date), 'YYYY-MM-DD HH24:MI:SS') as start_date_hour, EXTRACT('epoch' FROM (max((SELECT (max(M.create_date)) FROM mail_message M JOIN mail_message_mail_channel_rel R ON (R.mail_message_id = M.id) WHERE R.mail_channel_id = C.id))-C.create_date)) as duration, count(distinct P.id) as nbr_speaker, count(distinct M.id) as nbr_message, MAX(S.partner_id) as partner_id FROM mail_channel C JOIN mail_message_mail_channel_rel R ON (C.id = R.mail_channel_id) JOIN mail_message M ON (M.id = R.mail_message_id) JOIN mail_channel_partner S ON (S.channel_id = C.id) JOIN im_livechat_channel L ON (L.id = C.livechat_channel_id) LEFT JOIN res_partner P ON (M.author_id = P.id) GROUP BY C.id, C.name, C.livechat_channel_id, L.name, C.create_date, C.uuid ) """)
class Project(models.Model): _inherit = "project.project" @api.one @api.depends('percentage_satisfaction_task') def _compute_percentage_satisfaction_project(self): self.percentage_satisfaction_project = self.percentage_satisfaction_task @api.one @api.depends('tasks.rating_ids.rating') def _compute_percentage_satisfaction_task(self): activity = self.tasks.rating_get_grades() self.percentage_satisfaction_task = activity['great'] * 100 / sum( activity.values()) if sum(activity.values()) else -1 percentage_satisfaction_task = fields.Integer( compute='_compute_percentage_satisfaction_task', string='% Happy', store=True, default=-1) percentage_satisfaction_project = fields.Integer( compute="_compute_percentage_satisfaction_project", string="% Happy", store=True, default=-1) is_visible_happy_customer = fields.Boolean( string="Customer Satisfaction", default=False, help= "Display informations about rating of the project on kanban and form view. This buttons will only be displayed if at least a rating exists." ) @api.multi def action_view_task_rating(self): """ return the action to see all the rating about the tasks of the project """ action = self.env['ir.actions.act_window'].for_xml_id( 'rating', 'action_view_rating') return dict(action, domain=[('rating', '!=', -1), ('res_id', 'in', self.tasks.ids), ('res_model', '=', 'project.task')]) @api.multi def action_view_all_rating(self): """ return the action to see all the rating about the all sort of activity of the project (tasks, issues, ...) """ return self.action_view_task_rating()
class Invite(models.TransientModel): """ Wizard to invite partners (or channels) and make them followers. """ _name = 'mail.wizard.invite' _description = 'Invite wizard' @api.model def default_get(self, fields): result = super(Invite, self).default_get(fields) user_name = self.env.user.name_get()[0][1] model = result.get('res_model') res_id = result.get('res_id') if self._context.get('mail_invite_follower_channel_only'): result['send_mail'] = False if 'message' in fields and model and res_id: model_name = self.env['ir.model'].search([('model', '=', self.pool[model]._name)]).name_get()[0][1] document_name = self.env[model].browse(res_id).name_get()[0][1] message = _('<div><p>Hello,</p><p>%s invited you to follow %s document: %s.</p></div>') % (user_name, model_name, document_name) result['message'] = message elif 'message' in fields: result['message'] = _('<div><p>Hello,</p><p>%s invited you to follow a new document.</p></div>') % user_name return result res_model = fields.Char('Related Document Model', required=True, select=1, help='Model of the followed resource') res_id = fields.Integer('Related Document ID', select=1, help='Id of the followed resource') partner_ids = fields.Many2many('res.partner', string='Recipients', help="List of partners that will be added as follower of the current document.") channel_ids = fields.Many2many('mail.channel', string='Channels', help='List of channels that will be added as listeners of the current document.', domain=[('channel_type', '=', 'channel')]) message = fields.Html('Message') send_mail = fields.Boolean('Send Email', default=True, help="If checked, the partners will receive an email warning they have been added in the document's followers.") @api.multi def add_followers(self): email_from = self.env['mail.message']._get_default_from() for wizard in self: Model = self.env[wizard.res_model] document = Model.browse(wizard.res_id) # filter partner_ids to get the new followers, to avoid sending email to already following partners new_partners = wizard.partner_ids - document.message_partner_ids new_channels = wizard.channel_ids - document.message_channel_ids document.message_subscribe(new_partners.ids, new_channels.ids) model_ids = self.env['ir.model'].search([('model', '=', wizard.res_model)]) model_name = model_ids.name_get()[0][1] # send an email if option checked and if a message exists (do not send void emails) if wizard.send_mail and wizard.message and not wizard.message == '<br>': # when deleting the message, cleditor keeps a <br> message = self.env['mail.message'].create({ 'subject': _('Invitation to follow %s: %s') % (model_name, document.name_get()[0][1]), 'body': wizard.message, 'record_name': document.name_get()[0][1], 'email_from': email_from, 'reply_to': email_from, 'model': wizard.res_model, 'res_id': wizard.res_id, 'no_auto_thread': True, }) new_partners.with_context(auto_delete=True)._notify(message, force_send=True, user_signature=True) message.unlink() return {'type': 'ir.actions.act_window_close'}
class EventAnswer(models.Model): _name = 'event.answer' _order = 'sequence,id' name = fields.Char('Answer', required=True, translate=True) question_id = fields.Many2one('event.question', required=True, ondelete='cascade') sequence = fields.Integer(default=10)
class AccountAccountTag(models.Model): _name = 'account.account.tag' _description = 'Account Tag' name = fields.Char(required=True) applicability = fields.Selection([('accounts', 'Accounts'), ('taxes', 'Taxes')], required=True, default='accounts') color = fields.Integer('Color Index')
class HtmlFormField(models.Model): _name = "html.form.field" _description = "HTML Form Field" _order = "sequence asc" sequence = fields.Integer(string="Sequence") html_id = fields.Many2one('html.form', ondelete='cascade', string="HTML Form") model_id = fields.Many2one('ir.model', string="Model", readonly=True) model = fields.Char(related="model_id.model", string="Model Name", readonly=True) field_id = fields.Many2one( 'ir.model.fields', domain= "[('name','!=','create_date'),('name','!=','create_uid'),('name','!=','id'),('name','!=','write_date'),('name','!=','write_uid')]", string="Form Field") field_type = fields.Many2one('html.form.field.type', string="Field Type") field_label = fields.Char(string="Field Label") html_name = fields.Char(string="HTML Name") validation_format = fields.Char(string="Validation Format") setting_general_required = fields.Boolean(string="Required") setting_binary_file_type_filter = fields.Selection( [('image', 'Image'), ('audio', 'Audio')], string="File Type Filter") character_limit = fields.Integer(string="Character Limit", default="100") @api.model def create(self, values): sequence = self.env['ir.sequence'].get('sequence') values['sequence'] = sequence return super(HtmlFormField, self).create(values) @api.onchange('field_id') def _onchange_field_id(self): """Set the default field type, html_name and field label""" if self.field_id: self.field_type = self.env['html.form.field.type'].search([ ('data_type', '=', self.field_id.ttype), ('default', '=', True) ])[0].id self.html_name = self.field_id.name self.field_label = self.field_id.field_description
class AccountMoveLineReconcile(models.TransientModel): """ Account move line reconcile wizard, it checks for the write off the reconcile entry or directly reconcile. """ _name = 'account.move.line.reconcile' _description = 'Account move line reconcile' trans_nbr = fields.Integer(string='# of Transaction', readonly=True) credit = fields.Float(string='Credit amount', readonly=True, digits=0) debit = fields.Float(string='Debit amount', readonly=True, digits=0) writeoff = fields.Float(string='Write-Off amount', readonly=True, digits=0) company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.user.company_id) @api.model def default_get(self, fields): res = super(AccountMoveLineReconcile, self).default_get(fields) data = self.trans_rec_get() if 'trans_nbr' in fields: res.update({'trans_nbr': data['trans_nbr']}) if 'credit' in fields: res.update({'credit': data['credit']}) if 'debit' in fields: res.update({'debit': data['debit']}) if 'writeoff' in fields: res.update({'writeoff': data['writeoff']}) return res @api.multi def trans_rec_get(self): context = self._context or {} credit = debit = 0 lines = self.env['account.move.line'].browse(context.get('active_ids', [])) for line in lines: if not line.reconciled: credit += line.credit debit += line.debit precision = self.company_id.currency_id.decimal_places writeoff = float_round(debit - credit, precision_digits=precision) credit = float_round(credit, precision_digits=precision) debit = float_round(debit, precision_digits=precision) return {'trans_nbr': len(lines), 'credit': credit, 'debit': debit, 'writeoff': writeoff} @api.multi def trans_rec_addendum_writeoff(self): return self.env['account.move.line.reconcile.writeoff'].trans_rec_addendum() @api.multi def trans_rec_reconcile_partial_reconcile(self): return self.env['account.move.line.reconcile.writeoff'].trans_rec_reconcile_partial() @api.multi def trans_rec_reconcile_full(self): move_lines = self.env['account.move.line'].browse(self._context.get('active_ids', [])) move_lines.reconcile() return {'type': 'ir.actions.act_window_close'}
class MailMessageSubtype(models.Model): """ Class holding subtype definition for messages. Subtypes allow to tune the follower subscription, allowing only some subtypes to be pushed on the Wall. """ _name = 'mail.message.subtype' _description = 'Message subtypes' _order = 'sequence, id' name = fields.Char( 'Message Type', required=True, translate=True, help='Message subtype gives a more precise type on the message, ' 'especially for system notifications. For example, it can be ' 'a notification related to a new record (New), or to a stage ' 'change in a process (Stage change). Message subtypes allow to ' 'precisely tune the notifications the user want to receive on its wall.' ) description = fields.Text( 'Description', translate=True, help='Description that will be added in the message posted for this ' 'subtype. If void, the name will be added instead.') internal = fields.Boolean( 'Internal Only', help= 'Messages with internal subtypes will be visible only by employees, aka members of base_user group' ) parent_id = fields.Many2one( 'mail.message.subtype', string='Parent', ondelete='set null', help= 'Parent subtype, used for automatic subscription. This field is not ' 'correctly named. For example on a project, the parent_id of project ' 'subtypes refers to task-related subtypes.') relation_field = fields.Char( 'Relation field', help='Field used to link the related model to the subtype model when ' 'using automatic subscription on a related document. The field ' 'is used to compute getattr(related_document.relation_field).') res_model = fields.Char( 'Model', help= "Model the subtype applies to. If False, this subtype applies to all models." ) default = fields.Boolean('Default', default=True, help="Activated by default when subscribing.") sequence = fields.Integer('Sequence', default=1, help="Used to order subtypes.") hidden = fields.Boolean('Hidden', help="Hide the subtype in the follower options")
class HrEquipmentStage(models.Model): """ Model for case stages. This models the main stages of a Maintenance Request management flow. """ _name = 'hr.equipment.stage' _description = 'Maintenance Stage' _order = 'sequence, id' name = fields.Char('Name', required=True, translate=True) sequence = fields.Integer('Sequence', default=20) fold = fields.Boolean('Folded in Recruitment Pipe') done = fields.Boolean('Request Done')
class ImLivechatReportOperator(models.Model): """ Livechat Support Report on the Operator """ _name = "im_livechat.report.operator" _description = "Livechat Support Report" _order = 'livechat_channel_id, partner_id' _auto = False partner_id = fields.Many2one('res.partner', 'Operator', readonly=True) livechat_channel_id = fields.Many2one('im_livechat.channel', 'Channel', readonly=True) nbr_channel = fields.Integer('# of channel', readonly=True, group_operator="sum", help="Number of conversation") channel_id = fields.Many2one('mail.channel', 'Conversation', readonly=True) start_date = fields.Datetime('Start Date of session', readonly=True, help="Start date of the conversation") time_to_answer = fields.Float( 'Time to answer', digits=(16, 2), readonly=True, group_operator="avg", help="Average time to give the first answer to the visitor") duration = fields.Float('Average duration', digits=(16, 2), readonly=True, group_operator="avg", help="Duration of the conversation (in seconds)") def init(self, cr): # Note : start_date_hour must be remove when the read_group will allow grouping on the hour of a datetime. Don't forget to change the view ! tools.drop_view_if_exists(cr, 'im_livechat_report_operator') cr.execute(""" CREATE OR REPLACE VIEW im_livechat_report_operator AS ( SELECT row_number() OVER () AS id, P.id as partner_id, L.id as livechat_channel_id, count(C.id) as nbr_channel, C.id as channel_id, C.create_date as start_date, EXTRACT('epoch' FROM (max((SELECT (max(M.create_date)) FROM mail_message M JOIN mail_message_mail_channel_rel R ON (R.mail_message_id = M.id) WHERE R.mail_channel_id = C.id))-C.create_date)) as duration, EXTRACT('epoch' from ((SELECT min(M.create_date) FROM mail_message M, mail_message_mail_channel_rel R WHERE M.author_id=P.id AND R.mail_channel_id = C.id AND R.mail_message_id = M.id)-(SELECT min(M.create_date) FROM mail_message M, mail_message_mail_channel_rel R WHERE M.author_id IS NULL AND R.mail_channel_id = C.id AND R.mail_message_id = M.id))) as time_to_answer FROM im_livechat_channel_im_user O JOIN res_users U ON (O.user_id = U.id) JOIN res_partner P ON (U.partner_id = P.id) LEFT JOIN im_livechat_channel L ON (L.id = O.channel_id) LEFT JOIN mail_channel C ON (C.livechat_channel_id = L.id) GROUP BY P.id, L.id, C.id, C.create_date ) """)
class res_partner(models.Model): _name = 'res.partner' _inherit = 'res.partner' @api.multi def _purchase_invoice_count(self): PurchaseOrder = self.env['purchase.order'] Invoice = self.env['account.invoice'] for partner in self: partner.purchase_order_count = PurchaseOrder.search_count([('partner_id', 'child_of', partner.id)]) partner.supplier_invoice_count = Invoice.search_count([('partner_id', 'child_of', partner.id), ('type', '=', 'in_invoice')]) @api.model def _commercial_fields(self): return super(res_partner, self)._commercial_fields() property_purchase_currency_id = fields.Many2one( 'res.currency', string="Supplier Currency", company_dependent=True, help="This currency will be used, instead of the default one, for purchases from the current partner") purchase_order_count = fields.Integer(compute='_purchase_invoice_count', string='# of Purchase Order') supplier_invoice_count = fields.Integer(compute='_purchase_invoice_count', string='# Vendor Bills')
class RecruitmentDegree(models.Model): _name = "hr.recruitment.degree" _description = "Degree of Recruitment" _sql_constraints = [ ('name_uniq', 'unique (name)', 'The name of the Degree of Recruitment must be unique!') ] name = fields.Char("Degree", required=True, translate=True) sequence = fields.Integer( "Sequence", default=1, help="Gives the sequence order when displaying a list of degrees.")
class SaleOrder(models.Model): _inherit = 'sale.order' tasks_ids = fields.Many2many('project.task', compute='_compute_tasks_ids', string='Tasks associated to this sale') tasks_count = fields.Integer(string='Tasks', compute='_compute_tasks_ids') @api.multi @api.depends('order_line.product_id.project_id') def _compute_tasks_ids(self): for order in self: order.tasks_ids = self.env['project.task'].search([ ('sale_line_id', 'in', order.order_line.ids) ]) order.tasks_count = len(order.tasks_ids) @api.multi def action_view_task(self): self.ensure_one() imd = self.env['ir.model.data'] action = imd.xmlid_to_object('project.action_view_task') list_view_id = imd.xmlid_to_res_id('project.view_task_tree2') form_view_id = imd.xmlid_to_res_id('project.view_task_form2') result = { 'name': action.name, 'help': action.help, 'type': action.type, 'views': [[list_view_id, 'tree'], [False, 'kanban'], [form_view_id, 'form'], [False, 'graph'], [False, 'calendar'], [False, 'pivot'], [False, 'graph']], 'target': action.target, 'context': action.context, 'res_model': action.res_model, } if len(self.tasks_ids) > 1: result['domain'] = "[('id','in',%s)]" % self.tasks_ids.ids elif len(self.tasks_ids) == 1: result['views'] = [(form_view_id, 'form')] result['res_id'] = self.tasks_ids.id else: result = {'type': 'ir.actions.act_window_close'} return result
class HrDepartment(models.Model): _inherit = 'hr.department' def _compute_expense_to_approve(self): expense_data = self.env['hr.expense'].read_group( [('department_id', 'in', self.ids), ('state', '=', 'submit')], ['department_id'], ['department_id']) result = dict((data['department_id'][0], data['department_id_count']) for data in expense_data) for department in self: department.expense_to_approve_count = result.get(department.id, 0) expense_to_approve_count = fields.Integer( compute='_compute_expense_to_approve', string='Expenses to Approve')
class AccountingAssertTest(models.Model): _name = "accounting.assert.test" _order = "sequence" name = fields.Char(string='Test Name', required=True, index=True, translate=True) desc = fields.Text(string='Test Description', index=True, translate=True) code_exec = fields.Text(string='Python code', required=True, default=CODE_EXEC_DEFAULT) active = fields.Boolean(default=True) sequence = fields.Integer(default=10)
class product_template(models.Model): _inherit = ['product.template'] attachment_count = fields.Integer(compute='_compute_attachment_count', string="File") @api.multi def _compute_attachment_count(self): IrAttachment = self.env['ir.attachment'] for ptemplate in self: prod_tmpl_attach_count = IrAttachment.search_count([ ('res_model', '=', 'product.template'), ('res_id', 'in', ptemplate.ids) ]) prod_attach_count = IrAttachment.search_count([ ('res_model', '=', 'product.product'), ('res_id', 'in', ptemplate.product_variant_ids.ids) ]) ptemplate.attachment_count = prod_tmpl_attach_count + prod_attach_count @api.model def _get_product_template_type(self): res = super(product_template, self)._get_product_template_type() if 'digital' not in [item[0] for item in res]: res.append(('digital', 'Digital Content')) return res @api.multi def action_open_attachments(self): self.ensure_one() return { 'name': _('Digital Attachments'), 'domain': [ '|', '&', ('res_model', '=', 'product.product'), ('res_id', 'in', self.product_variant_ids.ids), '&', ('res_model', '=', self._name), ('res_id', '=', self.id) ], 'res_model': 'ir.attachment', 'type': 'ir.actions.act_window', 'view_mode': 'kanban,form', 'view_type': 'form', 'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, self.id), }