class HrContract(models.Model): _inherit = 'hr.contract' _description = 'Employee Contract' analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account') journal_id = fields.Many2one('account.journal', 'Salary Journal')
class AccountInvoiceLine(models.Model): """ Override AccountInvoice_line to add the link to the purchase order line it is related to""" _inherit = 'account.invoice.line' purchase_line_id = fields.Many2one('purchase.order.line', 'Purchase Order Line', ondelete='set null', index=True, readonly=True) purchase_id = fields.Many2one('purchase.order', related='purchase_line_id.order_id', string='Purchase Order', store=False, readonly=True, related_sudo=False, help='Associated Purchase Order. Filled in automatically when a PO is chosen on the vendor bill.')
class MrpProductionMessage(models.Model): _name = "mrp.message" _description = "Production Message" @api.model def _default_valid_until(self): return datetime.today() + relativedelta(days=7) name = fields.Text(compute='_get_note_first_line', store=True) message = fields.Html(required=True) product_tmpl_id = fields.Many2one('product.template', 'Product Template') product_id = fields.Many2one('product.product', string="Product") bom_id = fields.Many2one( 'mrp.bom', 'Bill of Material', domain= "['|', ('product_id', '=', product_id), ('product_tmpl_id.product_variant_ids','=', product_id)]" ) workcenter_id = fields.Many2one('mrp.workcenter', string='Work Center') valid_until = fields.Date('Validity Date', default=_default_valid_until, required=True) routing_id = fields.Many2one('mrp.routing', string='Routing') @api.depends('message') def _get_note_first_line(self): for message in self: message.name = (message.message and html2plaintext(message.message) or "").strip().replace('*', '').split("\n")[0] @api.multi def save(self): """ Used in a wizard-like form view, manual save button when in edit mode """ return True
class StockLocation(models.Model): _inherit = "stock.location" valuation_in_account_id = fields.Many2one( 'account.account', 'Stock Valuation Account (Incoming)', domain=[('internal_type', '=', 'other'), ('deprecated', '=', False)], help= "Used for real-time inventory valuation. When set on a virtual location (non internal type), " "this account will be used to hold the value of products being moved from an internal location " "into this location, instead of the generic Stock Output Account set on the product. " "This has no effect for internal locations.") valuation_out_account_id = fields.Many2one( 'account.account', 'Stock Valuation Account (Outgoing)', domain=[('internal_type', '=', 'other'), ('deprecated', '=', False)], help= "Used for real-time inventory valuation. When set on a virtual location (non internal type), " "this account will be used to hold the value of products being moved out of this location " "and into an internal location, instead of the generic Stock Output Account set on the product. " "This has no effect for internal locations.") def _should_be_valued(self): """ This method returns a boolean reflecting whether the products stored in `self` should be considered when valuating the stock of a company. """ self.ensure_one() if self.usage == 'internal' or (self.usage == 'transit' and self.company_id): return True return False
class RegistrationEditorLine(models.TransientModel): """Event Registration""" _name = "registration.editor.line" editor_id = fields.Many2one('registration.editor') sale_order_line_id = fields.Many2one('sale.order.line', string='Sales Order Line') event_id = fields.Many2one('event.event', string='Event', required=True) registration_id = fields.Many2one('event.registration', 'Original Registration') event_ticket_id = fields.Many2one('event.event.ticket', string='Event Ticket') email = fields.Char(string='Email') phone = fields.Char(string='Phone') name = fields.Char(string='Name', index=True) @api.multi def get_registration_data(self): self.ensure_one() return { 'event_id': self.event_id.id, 'event_ticket_id': self.event_ticket_id.id, 'partner_id': self.editor_id.sale_order_id.partner_id.id, 'name': self.name or self.editor_id.sale_order_id.partner_id.name, 'phone': self.phone or self.editor_id.sale_order_id.partner_id.phone, 'email': self.email or self.editor_id.sale_order_id.partner_id.email, 'origin': self.editor_id.sale_order_id.name, 'sale_order_id': self.editor_id.sale_order_id.id, 'sale_order_line_id': self.sale_order_line_id.id, }
class HrSalaryRuleCategory(models.Model): _name = 'hr.salary.rule.category' _description = 'Salary Rule Category' name = fields.Char(required=True, translate=True) code = fields.Char(required=True) parent_id = fields.Many2one( 'hr.salary.rule.category', string='Parent', help= "Linking a salary category to its parent is used only for the reporting purpose." ) children_ids = fields.One2many('hr.salary.rule.category', 'parent_id', string='Children') note = fields.Text(string='Description') company_id = fields.Many2one( 'res.company', string='Company', default=lambda self: self.env['res.company']._company_default_get()) @api.constrains('parent_id') def _check_parent_id(self): if not self._check_recursion(): raise ValidationError( _('Error! You cannot create recursive hierarchy of Salary Rule Category.' ))
class HrPayrollAdviceLine(models.Model): ''' Bank Advice Lines ''' _name = 'hr.payroll.advice.line' _description = 'Bank Advice Lines' advice_id = fields.Many2one('hr.payroll.advice', string='Bank Advice') name = fields.Char('Bank Account No.', required=True) ifsc_code = fields.Char(string='IFSC Code') employee_id = fields.Many2one('hr.employee', string='Employee', required=True) bysal = fields.Float(string='By Salary', digits=dp.get_precision('Payroll')) debit_credit = fields.Char(string='C/D', default='C') company_id = fields.Many2one('res.company', related='advice_id.company_id', string='Company', store=True) ifsc = fields.Boolean(related='advice_id.neft', string='IFSC') @api.onchange('employee_id') def onchange_employee_id(self): self.name = self.employee_id.bank_account_id.acc_number self.ifsc_code = self.employee_id.bank_account_id.bank_bic or ''
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=10, help="Gives the sequence order when displaying a list of stages.") job_id = fields.Many2one('hr.job', string='Job Specific', ondelete='cascade', help='Specific job that uses this stage. Other jobs will not use this stage.') requirements = fields.Text("Requirements") template_id = fields.Many2one( 'mail.template', "Automated Email", 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.") @api.model def default_get(self, fields): if self._context and self._context.get('default_job_id') and not self._context.get('hr_recruitment_stage_mono', False): context = dict(self._context) context.pop('default_job_id') self = self.with_context(context) return super(RecruitmentStage, self).default_get(fields)
class FleetVehicleLogServices(models.Model): _name = 'fleet.vehicle.log.services' _inherits = {'fleet.vehicle.cost': 'cost_id'} _description = 'Services for vehicles' @api.model def default_get(self, default_fields): res = super(FleetVehicleLogServices, self).default_get(default_fields) service = self.env.ref('fleet.type_service_service_8', raise_if_not_found=False) res.update({ 'date': fields.Date.context_today(self), 'cost_subtype_id': service and service.id or False, 'cost_type': 'services' }) return res purchaser_id = fields.Many2one('res.partner', 'Purchaser', domain="['|',('customer','=',True),('employee','=',True)]") inv_ref = fields.Char('Invoice Reference') vendor_id = fields.Many2one('res.partner', 'Vendor', domain="[('supplier','=',True)]") # we need to keep this field as a related with store=True because the graph view doesn't support # (1) to address fields from inherited table and (2) fields that aren't stored in database cost_amount = fields.Float(related='cost_id.amount', string='Amount', store=True) notes = fields.Text() cost_id = fields.Many2one('fleet.vehicle.cost', 'Cost', required=True, ondelete='cascade') @api.onchange('vehicle_id') def _onchange_vehicle(self): if self.vehicle_id: self.odometer_unit = self.vehicle_id.odometer_unit self.purchaser_id = self.vehicle_id.driver_id.id
class ReportEventRegistrationQuestions(models.Model): _name = "event.question.report" _auto = False attendee_id = fields.Many2one(comodel_name='event.registration', string='Registration') question_id = fields.Many2one(comodel_name='event.question', string='Question') answer_id = fields.Many2one(comodel_name='event.answer', string='Answer') event_id = fields.Many2one(comodel_name='event.event', string='Event') @api.model_cr def init(self): """ Event Question main report """ tools.drop_view_if_exists(self._cr, 'event_question_report') self._cr.execute(""" CREATE VIEW event_question_report AS ( SELECT att_answer.id as id, att_answer.event_registration_id as attendee_id, answer.question_id as question_id, answer.id as answer_id, question.event_id as event_id FROM event_registration_answer as att_answer LEFT JOIN event_answer as answer ON answer.id = att_answer.event_answer_id LEFT JOIN event_question as question ON question.id = answer.question_id GROUP BY attendee_id, event_id, question_id, answer_id, att_answer.id )""")
class EventTypeMail(models.Model): """ Template of event.mail to attach to event.type. Those will be copied upon all events created in that type to ease event creation. """ _name = 'event.type.mail' _description = 'Mail Scheduling on Event Type' event_type_id = fields.Many2one('event.type', string='Event Type', ondelete='cascade', required=True) interval_nbr = fields.Integer('Interval', default=1) interval_unit = fields.Selection([('now', 'Immediately'), ('hours', 'Hour(s)'), ('days', 'Day(s)'), ('weeks', 'Week(s)'), ('months', 'Month(s)')], string='Unit', default='hours', required=True) interval_type = fields.Selection([('after_sub', 'After each registration'), ('before_event', 'Before the event'), ('after_event', 'After the event')], string='Trigger', default="before_event", required=True) template_id = fields.Many2one( 'mail.template', string='Email Template', domain=[('model', '=', 'event.registration')], required=True, ondelete='restrict', help= 'This field contains the template of the mail that will be automatically sent' )
class grant_badge_wizard(models.TransientModel): """ Wizard allowing to grant a badge to a user""" _name = 'gamification.badge.user.wizard' user_id = fields.Many2one("res.users", string='User', required=True) badge_id = fields.Many2one("gamification.badge", string='Badge', required=True) comment = fields.Text('Comment') @api.multi def action_grant_badge(self): """Wizard action for sending a badge to a chosen user""" BadgeUser = self.env['gamification.badge.user'] uid = self.env.uid for wiz in self: if uid == wiz.user_id.id: raise exceptions.UserError(_('You can not grant a badge to yourself')) #create the badge BadgeUser.create({ 'user_id': wiz.user_id.id, 'sender_id': uid, 'badge_id': wiz.badge_id.id, 'comment': wiz.comment, })._send_badge() return True
class Notification(models.Model): _name = 'mail.notification' _table = 'mail_message_res_partner_needaction_rel' _rec_name = 'res_partner_id' _log_access = False _description = 'Message Notifications' mail_message_id = fields.Many2one( 'mail.message', 'Message', index=True, ondelete='cascade', required=True) res_partner_id = fields.Many2one( 'res.partner', 'Needaction Recipient', index=True, ondelete='cascade', required=True) is_read = fields.Boolean('Is Read', index=True) is_email = fields.Boolean('Sent by Email', index=True) email_status = fields.Selection([ ('ready', 'Ready to Send'), ('sent', 'Sent'), ('bounce', 'Bounced'), ('exception', 'Exception')], 'Email Status', default='ready', index=True) @api.model_cr def init(self): self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_res_partner_id_is_read_email_status_mail_message_id',)) if not self._cr.fetchone(): self._cr.execute('CREATE INDEX mail_notification_res_partner_id_is_read_email_status_mail_message_id ON mail_message_res_partner_needaction_rel (res_partner_id, is_read, email_status, mail_message_id)')
class LinkTrackerClick(models.Model): _inherit = "link.tracker.click" mail_stat_id = fields.Many2one('mail.mail.statistics', string='Mail Statistics') mass_mailing_id = fields.Many2one('mail.mass_mailing', string='Mass Mailing') mass_mailing_campaign_id = fields.Many2one('mail.mass_mailing.campaign', string='Mass Mailing Campaign') @api.model def add_click(self, code, ip, country_code, stat_id=False): res = super(LinkTrackerClick, self).add_click(code, ip, country_code, stat_id=stat_id) if stat_id: stat_sudo = self.env['mail.mail.statistics'].sudo().browse(stat_id) stat_sudo.set_opened() stat_sudo.set_clicked() return res def _get_click_values_from_route(self, route_values): click_values = super(LinkTrackerClick, self)._get_click_values_from_route(route_values) if route_values['stat_id']: mail_stat = self.env['mail.mail.statistics'].browse(route_values['stat_id']) click_values['mail_stat_id'] = mail_stat.id if mail_stat.mass_mailing_campaign_id: click_values['mass_mailing_campaign_id'] = mail_stat.mass_mailing_campaign_id.id if mail_stat.mass_mailing_id: click_values['mass_mailing_id'] = mail_stat.mass_mailing_id.id return click_values
class RecruitmentSource(models.Model): _name = "hr.recruitment.source" _description = "Source of Applicants" _inherits = {"utm.source": "source_id"} source_id = fields.Many2one('utm.source', "Source", ondelete='cascade', required=True) email = fields.Char(related='alias_id.display_name', string="Email", readonly=True) job_id = fields.Many2one('hr.job', "Job ID") alias_id = fields.Many2one('mail.alias', "Alias ID") @api.multi def create_alias(self): campaign = self.env.ref('hr_recruitment.utm_campaign_job') medium = self.env.ref('utm.utm_medium_email') for source in self: vals = { 'alias_parent_thread_id': source.job_id.id, 'alias_name': "%s+%s" % (source.job_id.alias_name or source.job_id.name, source.name), 'alias_defaults': { 'job_id': source.job_id.id, 'campaign_id': campaign.id, 'medium_id': medium.id, 'source_id': source.source_id.id, }, } source.alias_id = self.with_context(alias_model_name='hr.applicant', alias_parent_model_name='hr.job').env['mail.alias'].create(vals) source.name = source.source_id.name
class LunchCashMove(models.Model): """ Two types of cashmoves: payment (credit) or order (debit) """ _name = 'lunch.cashmove' _description = 'lunch cashmove' user_id = fields.Many2one('res.users', 'User', default=lambda self: self.env.uid) date = fields.Date('Date', required=True, default=fields.Date.context_today) amount = fields.Float( 'Amount', required=True, help= 'Can be positive (payment) or negative (order or payment if user wants to get his money back)' ) description = fields.Text('Description', help='Can be an order or a payment') order_id = fields.Many2one('lunch.order.line', 'Order', ondelete='cascade') state = fields.Selection([('order', 'Order'), ('payment', 'Payment')], 'Is an order or a payment', default='payment') @api.multi def name_get(self): return [(cashmove.id, '%s %s' % (_('Lunch Cashmove'), '#%d' % cashmove.id)) for cashmove in self]
class ResConfigSettings(models.TransientModel): _inherit = 'res.config.settings' def _get_crm_default_team_domain(self): if self.env.user.has_group('crm.group_use_lead'): return [('use_leads', '=', True)] else: return [('use_opportunities', '=', True)] crm_default_team_id = fields.Many2one( 'crm.team', string='Default Sales Channel', related='website_id.crm_default_team_id', domain=lambda self: self._get_crm_default_team_domain(), help= 'Default sales channel for new leads created through the Contact Us form.' ) crm_default_user_id = fields.Many2one( 'res.users', string='Default Salesperson', related='website_id.crm_default_user_id', domain=[('share', '=', False)], help= 'Default salesperson for new leads created through the Contact Us form.' )
class ResourceCalendarLeaves(models.Model): _name = "resource.calendar.leaves" _description = "Leave Detail" name = fields.Char('Reason') company_id = fields.Many2one( 'res.company', related='calendar_id.company_id', string="Company", readonly=True, store=True) calendar_id = fields.Many2one('resource.calendar', 'Working Hours') date_from = fields.Datetime('Start Date', required=True) date_to = fields.Datetime('End Date', required=True) tz = fields.Selection( _tz_get, string='Timezone', default=lambda self: self._context.get('tz') or self.env.user.tz or 'UTC', help="Timezone used when encoding the leave. It is used to correctly " "localize leave hours when computing time intervals.") resource_id = fields.Many2one( "resource.resource", 'Resource', help="If empty, this is a generic holiday for the company. If a resource is set, the holiday/leave is only for this resource") @api.constrains('date_from', 'date_to') def check_dates(self): if self.filtered(lambda leave: leave.date_from > leave.date_to): raise ValidationError(_('Error! leave start-date must be lower then leave end-date.')) @api.onchange('resource_id') def onchange_resource(self): if self.resource_id: self.calendar_id = self.resource_id.calendar_id
class ProductTemplate(models.Model): _inherit = "product.template" taxes_id = fields.Many2many('account.tax', 'product_taxes_rel', 'prod_id', 'tax_id', string='Customer Taxes', domain=[('type_tax_use', '=', 'sale')]) supplier_taxes_id = fields.Many2many('account.tax', 'product_supplier_taxes_rel', 'prod_id', 'tax_id', string='Vendor Taxes', domain=[('type_tax_use', '=', 'purchase')]) property_account_income_id = fields.Many2one( 'account.account', company_dependent=True, string="Income Account", oldname="property_account_income", domain=[('deprecated', '=', False)], help= "Keep this field empty to use the default value from the product category." ) property_account_expense_id = fields.Many2one( 'account.account', company_dependent=True, string="Expense Account", oldname="property_account_expense", domain=[('deprecated', '=', False)], help= "The expense is accounted for when a vendor bill is validated, except in anglo-saxon accounting with perpetual inventory valuation in which case the expense (Cost of Goods Sold account) is recognized at the customer invoice validation. If the field is empty, it uses the one defined in the product category." ) @api.multi def _get_product_accounts(self): return { 'income': self.property_account_income_id or self.categ_id.property_account_income_categ_id, 'expense': self.property_account_expense_id or self.categ_id.property_account_expense_categ_id } @api.multi def _get_asset_accounts(self): res = {} res['stock_input'] = False res['stock_output'] = False return res @api.multi def get_product_accounts(self, fiscal_pos=None): accounts = self._get_product_accounts() if not fiscal_pos: fiscal_pos = self.env['account.fiscal.position'] return fiscal_pos.map_accounts(accounts)
class FixedPutAwayStrategy(models.Model): _name = 'stock.fixed.putaway.strat' _order = 'sequence' putaway_id = fields.Many2one('product.putaway', 'Put Away Method', required=True) category_id = fields.Many2one('product.category', 'Product Category', required=True) fixed_location_id = fields.Many2one('stock.location', 'Location', required=True) sequence = fields.Integer('Priority', help="Give to the more specialized category, a higher priority to have them in top of the list.")
class CrossoveredBudget(models.Model): _name = "crossovered.budget" _description = "Budget" _inherit = ['mail.thread'] name = fields.Char('Budget Name', required=True, states={'done': [('readonly', True)]}) creating_user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user) date_from = fields.Date('Start Date', required=True, states={'done': [('readonly', True)]}) date_to = fields.Date('End Date', required=True, states={'done': [('readonly', True)]}) state = fields.Selection([('draft', 'Draft'), ('cancel', 'Cancelled'), ('confirm', 'Confirmed'), ('validate', 'Validated'), ('done', 'Done')], 'Status', default='draft', index=True, required=True, readonly=True, copy=False, track_visibility='always') crossovered_budget_line = fields.One2many( 'crossovered.budget.lines', 'crossovered_budget_id', 'Budget Lines', states={'done': [('readonly', True)]}, copy=True) company_id = fields.Many2one('res.company', 'Company', required=True, default=lambda self: self.env['res.company']. _company_default_get('account.budget.post')) @api.multi def action_budget_confirm(self): self.write({'state': 'confirm'}) @api.multi def action_budget_draft(self): self.write({'state': 'draft'}) @api.multi def action_budget_validate(self): self.write({'state': 'validate'}) @api.multi def action_budget_cancel(self): self.write({'state': 'cancel'}) @api.multi def action_budget_done(self): self.write({'state': 'done'})
class CrmLeadConvert2Task(models.TransientModel): """ wizard to convert a Lead into a Project task and move the Mail Thread """ _name = "crm.lead.convert2task" _inherit = 'crm.partner.binding' @api.model def default_get(self, fields): result = super(CrmLeadConvert2Task, self).default_get(fields) lead_id = self.env.context.get('active_id') if lead_id: result['lead_id'] = lead_id return result lead_id = fields.Many2one('crm.lead', string='Lead', domain=[('type', '=', 'lead')]) project_id = fields.Many2one('project.project', string='Project') @api.multi def action_lead_to_project_task(self): self.ensure_one() # get the lead to transform lead = self.lead_id partner_id = self._find_matching_partner() if not partner_id and (lead.partner_name or lead.contact_name): partner_id = lead.handle_partner_assignation()[lead.id] # create new project.task vals = { "name": lead.name, "description": lead.description, "email_from": lead.email_from, "project_id": self.project_id.id, "partner_id": partner_id, "user_id": None } task = self.env['project.task'].create(vals) # move the mail thread lead.message_change_thread(task) # move attachments attachments = self.env['ir.attachment'].search([ ('res_model', '=', 'crm.lead'), ('res_id', '=', lead.id) ]) attachments.write({'res_model': 'project.task', 'res_id': task.id}) # archive the lead lead.write({'active': False}) # return the action to go to the form view of the new Task view = self.env.ref('project.view_task_form2') return { 'name': 'Task created', 'view_type': 'form', 'view_mode': 'form', 'view_id': view.id, 'res_model': 'project.task', 'type': 'ir.actions.act_window', 'res_id': task.id, 'context': self.env.context }
class HrContract(models.Model): _inherit = 'hr.contract' car_id = fields.Many2one('fleet.vehicle', string='Company Car', domain=lambda self: self._get_available_cars_domain(), default=lambda self: self.env['fleet.vehicle'].search([('driver_id', '=', self.employee_id.address_home_id.id)], limit=1), track_visibility="onchange", help="Employee's company car.") car_atn = fields.Float(compute='_compute_car_atn_and_costs', string='ATN Company Car', store=True) company_car_total_depreciated_cost = fields.Float(compute='_compute_car_atn_and_costs', store=True) available_cars_amount = fields.Integer(compute='_compute_available_cars_amount', string='Number of available cars') new_car = fields.Boolean('Request a new car') new_car_model_id = fields.Many2one('fleet.vehicle.model', string="Model", domain=lambda self: self._get_possible_model_domain()) max_unused_cars = fields.Integer(compute='_compute_max_unused_cars') @api.depends('car_id', 'new_car', 'new_car_model_id', 'car_id.total_depreciated_cost', 'car_id.atn', 'new_car_model_id.default_atn', 'new_car_model_id.default_total_depreciated_cost', 'car_id.co2_fee', 'car_id.log_contracts', 'car_id.log_contracts.state', # YTI TODO: Store total_depreciated_costs on 'car_id.log_contracts.recurring_cost_amount_depreciated') # the fleet vehicle to avoid these dependencies def _compute_car_atn_and_costs(self): for contract in self: if not contract.new_car and contract.car_id: contract.car_atn = contract.car_id.atn contract.company_car_total_depreciated_cost = contract.car_id.total_depreciated_cost elif contract.new_car and contract.new_car_model_id: contract.car_atn = contract.new_car_model_id.default_atn contract.company_car_total_depreciated_cost = contract.new_car_model_id.default_total_depreciated_cost @api.depends('name') def _compute_available_cars_amount(self): for contract in self: contract.available_cars_amount = self.env['fleet.vehicle'].search_count([('driver_id', '=', False)]) @api.depends('name') def _compute_max_unused_cars(self): params = self.env['ir.config_parameter'].sudo() max_unused_cars = params.get_param('l10n_be_hr_payroll_fleet.max_unused_cars', default=1000) for contract in self: contract.max_unused_cars = int(max_unused_cars) @api.onchange('employee_id') def _onchange_employee_id(self): super(HrContract, self)._onchange_employee_id() self.car_id = self.env['fleet.vehicle'].search([('driver_id', '=', self.employee_id.address_home_id.id)], limit=1) return {'domain': {'car_id': self._get_available_cars_domain()}} @api.onchange('transport_mode') def _onchange_transport_mode(self): super(HrContract, self)._onchange_transport_mode() if self.transport_mode != 'company_car': self.car_id = False self.new_car_model_id = False def _get_available_cars_domain(self): return ['|', ('driver_id', '=', False), ('driver_id', '=', self.employee_id.address_home_id.id)] def _get_possible_model_domain(self): return [('can_be_requested', '=', True)]
class RepairFee(models.Model): _name = 'mrp.repair.fee' _description = 'Repair Fees Line' repair_id = fields.Many2one( 'mrp.repair', 'Repair Order Reference', index=True, ondelete='cascade', required=True) name = fields.Char('Description', index=True, required=True) product_id = fields.Many2one('product.product', 'Product') product_uom_qty = fields.Float('Quantity', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0) price_unit = fields.Float('Unit Price', required=True) product_uom = fields.Many2one('product.uom', 'Product Unit of Measure', required=True) price_subtotal = fields.Float('Subtotal', compute='_compute_price_subtotal', digits=0) tax_id = fields.Many2many('account.tax', 'repair_fee_line_tax', 'repair_fee_line_id', 'tax_id', 'Taxes') invoice_line_id = fields.Many2one('account.invoice.line', 'Invoice Line', copy=False, readonly=True) invoiced = fields.Boolean('Invoiced', copy=False, readonly=True) @api.one @api.depends('price_unit', 'repair_id', 'product_uom_qty', 'product_id') def _compute_price_subtotal(self): taxes = self.tax_id.compute_all(self.price_unit, self.repair_id.pricelist_id.currency_id, self.product_uom_qty, self.product_id, self.repair_id.partner_id) self.price_subtotal = taxes['total_excluded'] @api.onchange('repair_id', 'product_id', 'product_uom_qty') def onchange_product_id(self): """ On change of product it sets product quantity, tax account, name, uom of product, unit price and price subtotal. """ if not self.product_id: return partner = self.repair_id.partner_id pricelist = self.repair_id.pricelist_id if partner and self.product_id: self.tax_id = partner.property_account_position_id.map_tax(self.product_id.taxes_id, self.product_id, partner).ids if self.product_id: self.name = self.product_id.display_name self.product_uom = self.product_id.uom_id.id warning = False if not pricelist: warning = { 'title': _('No Pricelist!'), 'message': _('You have to select a pricelist in the Repair form !\n Please set one before choosing a product.')} else: price = pricelist.get_product_price(self.product_id, self.product_uom_qty, partner) if price is False: warning = { 'title': _('No valid pricelist line found !'), 'message': _("Couldn't find a pricelist line matching this product and quantity.\nYou have to change either the product, the quantity or the pricelist.")} else: self.price_unit = price if warning: return {'warning': warning}
class BadgeUser(models.Model): """User having received a badge""" _name = 'gamification.badge.user' _description = 'Gamification user badge' _order = "create_date desc" _rec_name = "badge_name" user_id = fields.Many2one('res.users', string="User", required=True, ondelete="cascade", index=True) sender_id = fields.Many2one('res.users', string="Sender", help="The user who has send the badge") badge_id = fields.Many2one('gamification.badge', string='Badge', required=True, ondelete="cascade", index=True) challenge_id = fields.Many2one( 'gamification.challenge', string='Challenge originating', help="If this badge was rewarded through a challenge") comment = fields.Text('Comment') badge_name = fields.Char(related='badge_id.name', string="Badge Name") create_date = fields.Datetime('Created', readonly=True) create_uid = fields.Many2one('res.users', string='Creator', readonly=True) def _send_badge(self): """Send a notification to a user for receiving a badge Does not verify constrains on badge granting. The users are added to the owner_ids (create badge_user if needed) The stats counters are incremented :param ids: list(int) of badge users that will receive the badge """ template = self.env.ref('gamification.email_template_badge_received') for badge_user in self: self.env['mail.thread'].message_post_with_template( template.id, model=badge_user._name, res_id=badge_user.id, composition_mode='mass_mail', partner_ids=badge_user.user_id.partner_id.ids, ) return True @api.model def create(self, vals): self.env['gamification.badge'].browse( vals['badge_id']).check_granting() return super(BadgeUser, self).create(vals)
class account_financial_report(models.Model): _name = "account.financial.report" _description = "Account Report" @api.multi @api.depends('parent_id', 'parent_id.level') def _get_level(self): '''Returns a dictionary with key=the ID of a record and value = the level of this record in the tree structure.''' for report in self: level = 0 if report.parent_id: level = report.parent_id.level + 1 report.level = level def _get_children_by_order(self): '''returns a recordset of all the children computed recursively, and sorted by sequence. Ready for the printing''' res = self children = self.search([('parent_id', 'in', self.ids)], order='sequence ASC') if children: for child in children: res += child._get_children_by_order() return res name = fields.Char('Report Name', required=True, translate=True) parent_id = fields.Many2one('account.financial.report', 'Parent') children_ids = fields.One2many('account.financial.report', 'parent_id', 'Account Report') sequence = fields.Integer('Sequence') level = fields.Integer(compute='_get_level', string='Level', store=True) type = fields.Selection([ ('sum', 'View'), ('accounts', 'Accounts'), ('account_type', 'Account Type'), ('account_report', 'Report Value'), ], 'Type', default='sum') account_ids = fields.Many2many('account.account', 'account_account_financial_report', 'report_line_id', 'account_id', 'Accounts') account_report_id = fields.Many2one('account.financial.report', 'Report Value') account_type_ids = fields.Many2many('account.account.type', 'account_account_financial_report_type', 'report_id', 'account_type_id', 'Account Types') sign = fields.Selection([(-1, 'Reverse balance sign'), (1, 'Preserve balance sign')], 'Sign on Reports', required=True, default=1, help='For accounts that are typically more debited than credited and that you would like to print as negative amounts in your reports, you should reverse the sign of the balance; e.g.: Expense account. The same applies for accounts that are typically more credited than debited and that you would like to print as positive amounts in your reports; e.g.: Income account.') display_detail = fields.Selection([ ('no_detail', 'No detail'), ('detail_flat', 'Display children flat'), ('detail_with_hierarchy', 'Display children with hierarchy') ], 'Display details', default='detail_flat') style_overwrite = fields.Selection([ (0, 'Automatic formatting'), (1, 'Main Title 1 (bold, underlined)'), (2, 'Title 2 (bold)'), (3, 'Title 3 (bold, smaller)'), (4, 'Normal Text'), (5, 'Italic Text (smaller)'), (6, 'Smallest Text'), ], 'Financial Report Style', default=0, help="You can set up here the format you want this record to be displayed. If you leave the automatic formatting, it will be computed based on the financial reports hierarchy (auto-computed field 'level').")
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 Sessions', 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)") @api.model_cr def init(self): # 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(self.env.cr, 'im_livechat_report_operator') self.env.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 SaleQuoteOption(models.Model): _name = "sale.quote.option" _description = "Quotation Option" template_id = fields.Many2one('sale.quote.template', 'Quotation Template Reference', ondelete='cascade', index=True, required=True) name = fields.Text('Description', required=True, translate=True) product_id = fields.Many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True) layout_category_id = fields.Many2one('sale.layout_category', string='Section') website_description = fields.Html('Option Description', translate=html_translate, sanitize_attributes=False) price_unit = fields.Float('Unit Price', required=True, digits=dp.get_precision('Product Price')) discount = fields.Float('Discount (%)', digits=dp.get_precision('Discount')) uom_id = fields.Many2one('product.uom', 'Unit of Measure ', required=True) quantity = fields.Float('Quantity', required=True, digits=dp.get_precision('Product UoS'), default=1) @api.onchange('product_id') def _onchange_product_id(self): if not self.product_id: return product = self.product_id self.price_unit = product.list_price self.website_description = product.product_tmpl_id.quote_description self.name = product.name self.uom_id = product.uom_id domain = { 'uom_id': [('category_id', '=', self.product_id.uom_id.category_id.id)] } return {'domain': domain} @api.onchange('uom_id') def _onchange_product_uom(self): if not self.product_id: return if not self.uom_id: self.price_unit = 0.0 return if self.uom_id.id != self.product_id.uom_id.id: self.price_unit = self.product_id.uom_id._compute_price( self.price_unit, self.uom_id)
class Sponsor(models.Model): _name = "event.sponsor" _description = '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)
class HrContract(models.Model): """ Employee contract based on the visa, work permits allows to configure different Salary structure """ _inherit = 'hr.contract' _description = 'Employee Contract' struct_id = fields.Many2one('hr.payroll.structure', string='Salary Structure') schedule_pay = fields.Selection( [ ('monthly', 'Monthly'), ('quarterly', 'Quarterly'), ('semi-annually', 'Semi-annually'), ('annually', 'Annually'), ('weekly', 'Weekly'), ('bi-weekly', 'Bi-weekly'), ('bi-monthly', 'Bi-monthly'), ], string='Scheduled Pay', index=True, default='monthly', help="Defines the frequency of the wage payment.") resource_calendar_id = fields.Many2one(required=True, help="Employee's working schedule.") @api.multi def get_all_structures(self): """ @return: the structures linked to the given contracts, ordered by hierachy (parent=False first, then first level children and so on) and without duplicata """ structures = self.mapped('struct_id') if not structures: return [] # YTI TODO return browse records return list(set(structures._get_parent_structure().ids)) @api.multi def get_attribute(self, code, attribute): return self.env['hr.contract.advantage.template'].search( [('code', '=', code)], limit=1)[attribute] @api.multi def set_attribute_value(self, code, active): for contract in self: if active: value = self.env['hr.contract.advantage.template'].search( [('code', '=', code)], limit=1).default_value contract[code] = value else: contract[code] = 0.0