Exemplo n.º 1
0
class Task(models.Model):
    _name = "project.task"
    _description = "Task"
    _date_name = "date_start"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _mail_post_access = 'read'
    _order = "priority desc, sequence, date_start, name, id"

    @api.model
    def default_get(self, field_list):
        """ Set 'date_assign' if user_id is set. """
        result = super(Task, self).default_get(field_list)
        if 'user_id' in result:
            result['date_assign'] = fields.Datetime.now()
        return result

    def _get_default_partner(self):
        if 'default_project_id' in self.env.context:
            default_project_id = self.env['project.project'].browse(
                self.env.context['default_project_id'])
            return default_project_id.exists().partner_id

    def _get_default_stage_id(self):
        """ Gives default stage_id """
        project_id = self.env.context.get('default_project_id')
        if not project_id:
            return False
        return self.stage_find(project_id, [('fold', '=', False)])

    @api.model
    def _read_group_stage_ids(self, stages, domain, order):
        search_domain = [('id', 'in', stages.ids)]
        if 'default_project_id' in self.env.context:
            search_domain = [
                '|',
                ('project_ids', '=', self.env.context['default_project_id'])
            ] + search_domain

        stage_ids = stages._search(search_domain,
                                   order=order,
                                   access_rights_uid=SUPERUSER_ID)
        return stages.browse(stage_ids)

    active = fields.Boolean(default=True)
    name = fields.Char(string='Task Title',
                       track_visibility='always',
                       required=True,
                       index=True)
    description = fields.Html(string='Description')
    priority = fields.Selection([('0', 'Normal'), ('1', 'High')],
                                default='0',
                                index=True)
    sequence = fields.Integer(
        string='Sequence',
        index=True,
        default=10,
        help="Gives the sequence order when displaying a list of tasks.")
    stage_id = fields.Many2one('project.task.type',
                               string='Stage',
                               track_visibility='onchange',
                               index=True,
                               default=_get_default_stage_id,
                               group_expand='_read_group_stage_ids',
                               domain="[('project_ids', '=', project_id)]",
                               copy=False)
    tag_ids = fields.Many2many('project.tags',
                               string='Tags',
                               oldname='categ_ids')
    kanban_state = fields.Selection(
        [('normal', 'In Progress'), ('done', 'Ready for next stage'),
         ('blocked', 'Blocked')],
        string='Kanban State',
        default='normal',
        track_visibility='onchange',
        required=True,
        copy=False,
        help="A task's kanban state indicates special situations affecting it:\n"
        " * Normal is the default situation\n"
        " * Blocked indicates something is preventing the progress of this task\n"
        " * Ready for next stage indicates the task is ready to be pulled to the next stage"
    )
    create_date = fields.Datetime(index=True)
    write_date = fields.Datetime(
        index=True
    )  #not displayed in the view but it might be useful with base_action_rule module (and it needs to be defined first for that)
    date_start = fields.Datetime(string='Starting Date',
                                 default=fields.Datetime.now,
                                 index=True,
                                 copy=False)
    date_end = fields.Datetime(string='Ending Date', index=True, copy=False)
    date_assign = fields.Datetime(string='Assigning Date',
                                  index=True,
                                  copy=False,
                                  readonly=True)
    date_deadline = fields.Date(string='Deadline', index=True, copy=False)
    date_last_stage_update = fields.Datetime(string='Last Stage Update',
                                             default=fields.Datetime.now,
                                             index=True,
                                             copy=False,
                                             readonly=True)
    project_id = fields.Many2one(
        'project.project',
        string='Project',
        default=lambda self: self.env.context.get('default_project_id'),
        index=True,
        track_visibility='onchange',
        change_default=True)
    notes = fields.Text(string='Notes')
    planned_hours = fields.Float(
        string='Initially Planned Hours',
        help=
        'Estimated time to do the task, usually set by the project manager when the task is in draft state.'
    )
    remaining_hours = fields.Float(
        string='Remaining Hours',
        digits=(16, 2),
        help=
        "Total remaining time, can be re-estimated periodically by the assignee of the task."
    )
    user_id = fields.Many2one('res.users',
                              string='Assigned to',
                              default=lambda self: self.env.uid,
                              index=True,
                              track_visibility='always')
    partner_id = fields.Many2one('res.partner',
                                 string='Customer',
                                 default=_get_default_partner)
    manager_id = fields.Many2one('res.users',
                                 string='Project Manager',
                                 related='project_id.user_id',
                                 readonly=True)
    company_id = fields.Many2one(
        'res.company',
        string='Company',
        default=lambda self: self.env['res.company']._company_default_get())
    color = fields.Integer(string='Color Index')
    user_email = fields.Char(related='user_id.email',
                             string='User Email',
                             readonly=True)
    attachment_ids = fields.One2many(
        'ir.attachment',
        'res_id',
        domain=lambda self: [('res_model', '=', self._name)],
        auto_join=True,
        string='Attachments')
    # In the domain of displayed_image_id, we couln't use attachment_ids because a one2many is represented as a list of commands so we used res_model & res_id
    displayed_image_id = fields.Many2one(
        'ir.attachment',
        domain=
        "[('res_model', '=', 'project.task'), ('res_id', '=', id), ('mimetype', 'ilike', 'image')]",
        string='Displayed Image')
    legend_blocked = fields.Char(related='stage_id.legend_blocked',
                                 string='Kanban Blocked Explanation',
                                 readonly=True)
    legend_done = fields.Char(related='stage_id.legend_done',
                              string='Kanban Valid Explanation',
                              readonly=True)
    legend_normal = fields.Char(related='stage_id.legend_normal',
                                string='Kanban Ongoing Explanation',
                                readonly=True)
    employee = fields.Many2one('hr.employee', 'employee')
    checklist1 = fields.Many2many('project.checklist1', string='제조')
    checklist2 = fields.Many2many('project.checklist2', string='전장')
    checklist3 = fields.Many2many('project.checklist3', string='제어')
    checklist4 = fields.Many2many('project.checklist4', string='설계')
    checklist5 = fields.Many2one('project.checklist5', string='유닛')
    overlap = fields.Char('check', default='check')
    department_id = fields.Many2one('hr.department', string='부서', store=True)
    check_department = fields.Boolean('동일부서확인',
                                      compute='_compute_check_department',
                                      search='_search_department')

    @api.multi
    def name_get(self):
        result = []
        for record in self:
            name = record.name
            if record.checklist5:
                name = '[' + str(record.checklist5.name) + ']' + record.name
            result.append((record.id, name))
        return result

    @api.depends('department_id')
    def _compute_check_department(self):
        _logger.warning('check user.department_id == task.department_id')

    @api.multi
    def _search_department(self, operator, value):
        user_dep = self.env['hr.employee'].search([
            ('user_id', '=', self.env.uid)
        ]).department_id.id
        return [('department_id', '=', user_dep)]

    @api.onchange('project_id')
    def _onchange_project(self):
        if self.project_id:
            self.partner_id = self.project_id.partner_id
            self.stage_id = self.stage_find(self.project_id.id,
                                            [('fold', '=', False)])
        else:
            self.stage_id = False

    @api.onchange('user_id')
    def _onchange_user(self):
        if self.user_id:
            self.date_start = fields.Datetime.now()

    @api.multi
    def copy(self, default=None):
        if default is None:
            default = {}
        if not default.get('name'):
            default['name'] = _("%s (copy)") % self.name
        if 'remaining_hours' not in default:
            default['remaining_hours'] = self.planned_hours
        return super(Task, self).copy(default)

    @api.constrains('date_start', 'date_end')
    def _check_dates(self):
        if any(
                self.filtered(lambda task: task.date_start and task.date_end
                              and task.date_start > task.date_end)):
            raise ValidationError(
                _('Error ! Task starting date must be lower than its ending date.'
                  ))

    # Override view according to the company definition
    @api.model
    def fields_view_get(self,
                        view_id=None,
                        view_type='form',
                        toolbar=False,
                        submenu=False):
        # read uom as admin to avoid access rights issues, e.g. for portal/share users,
        # this should be safe (no context passed to avoid side-effects)
        obj_tm = self.env.user.company_id.project_time_mode_id
        tm = obj_tm and obj_tm.name or 'Hours'

        res = super(Task, self).fields_view_get(view_id=view_id,
                                                view_type=view_type,
                                                toolbar=toolbar,
                                                submenu=submenu)

        # read uom as admin to avoid access rights issues, e.g. for portal/share users,
        # this should be safe (no context passed to avoid side-effects)
        obj_tm = self.env.user.company_id.project_time_mode_id
        # using get_object to get translation value
        uom_hour = self.env.ref('product.product_uom_hour', False)
        if not obj_tm or not uom_hour or obj_tm.id == uom_hour.id:
            return res

        eview = etree.fromstring(res['arch'])

        # if the project_time_mode_id is not in hours (so in days), display it as a float field
        def _check_rec(eview):
            if eview.attrib.get('widget', '') == 'float_time':
                eview.set('widget', 'float')
            for child in eview:
                _check_rec(child)
            return True

        _check_rec(eview)

        res['arch'] = etree.tostring(eview)

        # replace reference of 'Hours' to 'Day(s)'
        for f in res['fields']:
            # TODO this NOT work in different language than english
            # the field 'Initially Planned Hours' should be replaced by 'Initially Planned Days'
            # but string 'Initially Planned Days' is not available in translation
            if 'Hours' in res['fields'][f]['string']:
                res['fields'][f]['string'] = res['fields'][f][
                    'string'].replace('Hours', obj_tm.name)
        return res

    @api.model
    def get_empty_list_help(self, help):
        self = self.with_context(
            empty_list_help_id=self.env.context.get('default_project_id'),
            empty_list_help_model='project.project',
            empty_list_help_document_name=_("tasks"))
        return super(Task, self).get_empty_list_help(help)

    # ----------------------------------------
    # Case management
    # ----------------------------------------

    def stage_find(self, section_id, domain=[], order='sequence'):
        """ Override of the base.stage method
            Parameter of the stage search taken from the lead:
            - section_id: if set, stages must belong to this section or
              be a default stage; if not set, stages must be default
              stages
        """
        # collect all section_ids
        section_ids = []
        if section_id:
            section_ids.append(section_id)
        section_ids.extend(self.mapped('project_id').ids)
        search_domain = []
        if section_ids:
            search_domain = [('|')] * (len(section_ids) - 1)
            for section_id in section_ids:
                search_domain.append(('project_ids', '=', section_id))
        search_domain += list(domain)
        # perform search, return the first found
        return self.env['project.task.type'].search(search_domain,
                                                    order=order,
                                                    limit=1).id

    # ------------------------------------------------
    # CRUD overrides
    # ------------------------------------------------

    @api.model
    def create(self, vals):
        # context: no_log, because subtype already handle this
        context = dict(self.env.context, mail_create_nolog=True)

        # for default stage
        if vals.get('project_id') and not context.get('default_project_id'):
            context['default_project_id'] = vals.get('project_id')
        # user_id change: update date_assign
        if vals.get('user_id'):
            vals['date_assign'] = fields.Datetime.now()
        task = super(Task, self.with_context(context)).create(vals)
        user_dep = self.env['hr.employee'].search([
            ('user_id', '=', self.env.uid)
        ]).department_id.id
        task.department_id = user_dep
        return task

    @api.multi
    def write(self, vals):
        now = fields.Datetime.now()
        # stage change: update date_last_stage_update
        if 'stage_id' in vals:
            vals['date_last_stage_update'] = now
            # reset kanban state when changing stage
            if 'kanban_state' not in vals:
                vals['kanban_state'] = 'normal'
        # user_id change: update date_assign
        if vals.get('user_id'):
            vals['date_assign'] = now

        result = super(Task, self).write(vals)

        return result

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

    @api.multi
    def _track_template(self, tracking):
        res = super(Task, self)._track_template(tracking)
        test_task = self[0]
        changes, tracking_value_ids = tracking[test_task.id]
        if 'stage_id' in changes and test_task.stage_id.mail_template_id:
            res['stage_id'] = (test_task.stage_id.mail_template_id, {
                'composition_mode': 'mass_mail'
            })
        return res

    @api.multi
    def _track_subtype(self, init_values):
        self.ensure_one()
        if 'kanban_state' in init_values and self.kanban_state == 'blocked':
            return 'project.mt_task_blocked'
        elif 'kanban_state' in init_values and self.kanban_state == 'done':
            return 'project.mt_task_ready'
        elif 'user_id' in init_values and self.user_id:  # assigned -> new
            return 'project.mt_task_new'
        elif 'stage_id' in init_values and self.stage_id and self.stage_id.sequence <= 1:  # start stage -> new
            return 'project.mt_task_new'
        elif 'stage_id' in init_values:
            return 'project.mt_task_stage'
        return super(Task, self)._track_subtype(init_values)

    @api.multi
    def _notification_recipients(self, message, groups):
        """ Handle project users and managers recipients that can convert assign
        tasks and create new one directly from notification emails. """
        groups = super(Task, self)._notification_recipients(message, groups)

        self.ensure_one()
        if not self.user_id:
            take_action = self._notification_link_helper('assign')
            project_actions = [{'url': take_action, 'title': _('I take it')}]
        else:
            new_action_id = self.env.ref('project.action_view_task').id
            new_action = self._notification_link_helper(
                'new', action_id=new_action_id)
            project_actions = [{'url': new_action, 'title': _('New Task')}]

        new_group = ('group_project_user',
                     lambda partner: bool(partner.user_ids) and any(
                         user.has_group('project.group_project_user')
                         for user in partner.user_ids), {
                             'actions': project_actions,
                         })

        return [new_group] + groups

    @api.model
    def message_get_reply_to(self, res_ids, default=None):
        """ Override to get the reply_to of the parent project. """
        tasks = self.sudo().browse(res_ids)
        project_ids = tasks.mapped('project_id').ids
        aliases = self.env['project.project'].message_get_reply_to(
            project_ids, default=default)
        return {
            task.id: aliases.get(task.project_id.id, False)
            for task in tasks
        }

    @api.multi
    def email_split(self, msg):
        email_list = tools.email_split((msg.get('to') or '') + ',' +
                                       (msg.get('cc') or ''))
        # check left-part is not already an alias
        aliases = self.mapped('project_id.alias_name')
        return filter(lambda x: x.split('@')[0] not in aliases, email_list)

    @api.model
    def message_new(self, msg, custom_values=None):
        """ Override to updates the document according to the email. """
        if custom_values is None:
            custom_values = {}
        defaults = {
            'name': msg.get('subject'),
            'planned_hours': 0.0,
            'partner_id': msg.get('author_id')
        }
        defaults.update(custom_values)

        res = super(Task, self).message_new(msg, custom_values=defaults)
        task = self.browse(res)
        email_list = task.email_split(msg)
        partner_ids = filter(
            None, task._find_partner_from_emails(email_list,
                                                 force_create=False))
        task.message_subscribe(partner_ids)
        return res

    @api.multi
    def message_update(self, msg, update_vals=None):
        """ Override to update the task according to the email. """
        if update_vals is None:
            update_vals = {}
        maps = {
            'cost': 'planned_hours',
        }
        for line in msg['body'].split('\n'):
            line = line.strip()
            res = tools.command_re.match(line)
            if res:
                match = res.group(1).lower()
                field = maps.get(match)
                if field:
                    try:
                        update_vals[field] = float(res.group(2).lower())
                    except (ValueError, TypeError):
                        pass

        email_list = self.email_split(msg)
        partner_ids = filter(
            None, self._find_partner_from_emails(email_list,
                                                 force_create=False))
        self.message_subscribe(partner_ids)
        return super(Task, self).message_update(msg, update_vals=update_vals)

    @api.multi
    def message_get_suggested_recipients(self):
        recipients = super(Task, self).message_get_suggested_recipients()
        for task in self.filtered('partner_id'):
            reason = _('Customer Email') if task.partner_id.email else _(
                'Customer')
            task._message_add_suggested_recipient(recipients,
                                                  partner=task.partner_id,
                                                  reason=reason)
        return recipients

    @api.multi
    def message_get_email_values(self, notif_mail=None):
        res = super(Task, self).message_get_email_values(notif_mail=notif_mail)
        headers = {}
        if res.get('headers'):
            try:
                headers.update(safe_eval(res['headers']))
            except Exception:
                pass
        if self.project_id:
            current_objects = filter(
                None,
                headers.get('X-Odoo-Objects', '').split(','))
            current_objects.insert(0,
                                   'project.project-%s, ' % self.project_id.id)
            headers['X-Odoo-Objects'] = ','.join(current_objects)
        if self.tag_ids:
            headers['X-Odoo-Tags'] = ','.join(self.tag_ids.mapped('name'))
        res['headers'] = repr(headers)
        return res
Exemplo n.º 2
0
class ImportDigitalAtasWizard(models.TransientModel):

    _name = 'import.digital.atas.wizard'

    delegated_type_id = fields.Many2one(
        comodel_name='cci_international.delegated_type',
        string='ATA Carnet Type',
        required=True,
        domain=[('section', '=', 'ata_carnet'), ('digital', '=', True)],
        help='Type of eATA to Import')
    input_file = fields.Binary(
        string='XML File',
        required=True,
        help='The XML file from eATA platform',
    )
    state = fields.Selection([('step1', 'try_import'),
                              ('step2', 'show_errors')],
                             default='step1')
    text_errors = fields.Text(string='Errors')
    rejected = fields.Integer(string='Rejected eATAs')
    imported_atas = fields.Integer(string='Imported eATAs')
    new_ids = []  # list of the ID of created ATA Carnets

    @api.multi
    def import_digital_atas(self):
        self.new_ids = []
        inputdata = base64.decodestring(self.input_file)
        inputdata = inputdata.decode(
            'utf-8'
        )  # 2015-03-04 : no more utf-16 but utf-8 as at the beginning

        # store the results
        self.rejected = 0
        self.imported_eatas = 0
        self.text_errors = ''
        errors = []
        created_codes = []

        # objects necessary for the import
        SaleOrder = self.env['sale.order']
        DelegatedType = self.env['cci_international.delegated_type']
        ResPartner = self.env['res.partner']
        ResCountryGroup = self.env['res.country.group']
        #CCIZip = self.env['cci.zip']

        root = objectify.fromstring(inputdata)
        # check if there is CARNET subElements
        if not hasattr(
                root, 'CARNET'
        ):  ### the file is empty OR this is not a certificates'file
            raise UserError(
                _('The given file is either empty or NOT an eATA XML file.\nPlease check your parameters.'
                  ))

        for carnet in root.CARNET:
            internal_id = str(carnet.ID)
            if len(internal_id) > 0 and not (internal_id[-1:] == 'D'):
                if str(carnet.ChamberNumber) == '87':
                    full_customer_number = str(carnet.CustomerNumber)
                    if full_customer_number[0:4] == 'cci_':
                        # searching partner by CustomerNumber
                        partners = ResPartner.search([
                            ('id', '=', int(full_customer_number[4:])),
                            ('active', '=', True)
                        ])
                        if len(partners) == 1:
                            partner = partners[0]
                            existing_atas = SaleOrder.search([
                                ('digital_ref', '=', internal_id),
                                ('section', '=', 'ata_carnet')
                            ])
                            # the search on digital_number is not enough because we have a serie of ATA Carnet without digital_number, so search on name also
                            if not existing_atas:
                                existing_atas = SaleOrder.search([
                                    ('internal_ref', '=', carnet.CarnetNumber
                                     or ''), ('section', '=', 'ata_carnet')
                                ])  # so we search also on 'name'
                                if not existing_atas:
                                    # extraction of countries
                                    #country_ids = []
                                    #if hasattr(carnet.DestinationList,'Destination'):
                                    #    for destination in carnet.DestinationList.Destination:
                                    #        isocode3 = str(destination)
                                    #        if len(isocode3) == 3:
                                    #            search_country_ids = obj_country.search(cr,uid,[('isocode3','=',isocode3)])
                                    #            if len(search_country_ids) == 1:
                                    #                country_ids.append(search_country_ids[0])
                                    # search for usage
                                    if str(carnet.IntendedUseList.IntendedUse
                                           ) == 'Other':
                                        usage_name = str(
                                            carnet.IntendedUseList.
                                            IntendedUseOther)
                                    else:
                                        usage_name = str(
                                            carnet.IntendedUseList.IntendedUse)
                                    customerRef = str(
                                        carnet.CustomerRef)  # to unicode
                                    if customerRef:
                                        while '  ' in customerRef:
                                            customerRef = customerRef.replace(
                                                '  ', ' ')
                                    ata_data = {}
                                    ata_data['section'] = 'ata_carnet'
                                    ata_data[
                                        'delegated_type_id'] = self.delegated_type_id.id
                                    ata_data['internal_ref'] = str(
                                        carnet.CarnetNumber
                                        or b'').replace(' ', '')
                                    ata_data['date_order'] = str(
                                        carnet.AcceptedDateTime
                                    )[6:10] + "-" + str(
                                        carnet.AcceptedDateTime
                                    )[3:5] + "-" + str(
                                        carnet.AcceptedDateTime)[0:2]
                                    ata_data['ata_validity_date'] = str(
                                        carnet.ExpiryDateTime
                                    )[6:10] + '-' + str(
                                        carnet.ExpiryDateTime
                                    )[3:5] + '-' + str(
                                        carnet.ExpiryDateTime)[0:2]
                                    ata_data['partner_id'] = partner.id
                                    ata_data['asker_name'] = str(
                                        carnet.Holder.Applicant)
                                    ata_data['asker_street'] = ''
                                    ata_data['asker_city'] = ''
                                    ata_data['sender_name'] = ''
                                    ata_data['sender_street'] = ''
                                    ata_data['sender_city'] = ''
                                    ata_data['state'] = 'draft'
                                    ata_data['goods_desc'] = ''
                                    ata_data['goods_value'] = float(
                                        str(carnet.TotalValue).replace(
                                            ',', '.'))
                                    ata_data['copies'] = 0
                                    ata_data['originals'] = 1
                                    ata_data['digital_ref'] = internal_id
                                    ata_data['client_order_ref'] = customerRef
                                    ata_data['ata_usage'] = usage_name
                                    tous_les_pays = ResCountryGroup.search([
                                        ('name', '=', 'Tous les Pays')
                                    ])
                                    if len(tous_les_pays) == 1:
                                        ata_data[
                                            'ata_area_id'] = tous_les_pays.id
                                    else:
                                        tous_les_pays = ResCountryGroup.search(
                                            [()])
                                        ata_data[
                                            'ata_area_id'] = tous_les_pays[
                                                0].id
                                    ata_data['ata_warranty'] = 50.0
                                    if hasattr(carnet.Deposit,
                                               'DepositAmount'):
                                        if carnet.Deposit.DepositAmount:
                                            ata_data['warranty'] = 0.0
                                            ata_data['own_risk'] = True
                                            ata_data[
                                                'internal_comments'] = u'Caution de %.2f euros' % float(
                                                    str(carnet.Deposit.
                                                        DepositAmount
                                                        or '0,0').replace(
                                                            ',', '.'))
                                    ata_data['ata_pages_initial'] = int(
                                        carnet.TotalNumberOfPages or '0')
                                    ata_data['ata_pages_additional'] = int(
                                        carnet.NumberOfExtraPages or '0')
                                    new_ata = SaleOrder.create(ata_data)
                                    self.new_ids.append(new_ata.id)
                                    self.imported_atas += 1
                                else:
                                    self.rejected += 1
                                    errors.append(
                                        'Name : %s - Already used name-number '
                                        % (carnet.CarnetNumber or '--none--'))
                            else:
                                self.rejected += 1
                                errors.append(
                                    'InternalID : %s - Already used digital number '
                                    % internal_id)
                        else:
                            self.rejected += 1
                            errors.append(
                                'InternalID : %s - Wrong Customer ID : \'%s\''
                                % (str(internal_id), full_customer_number))
                    else:
                        self.rejected += 1
                        errors.append(
                            'InternalID : %s - Unknown Customer Number : \'%s\''
                            % (str(internal_id), full_customer_number))
                else:
                    self.rejected += 1
                    errors.append(
                        'InternalID : %s - Wrong ChamberNumber : \'%s\'' %
                        (str(internal_id), str(carnet.ChamberNumber)))
            else:
                # duplicata of an existing ATA: nothing to DO
                errors.append('InternalID : %s - Duplicata => ignored' %
                              str(internal_id))
        # give the result to the user
        self.state = 'step2'
        if self.rejected > 0:
            self.text_errors = '\n'.join(errors)
        else:
            self.text_errors = 'No ERRORS at ALL !\n\nGreat job !'
        print('\n-----------ERRORS---------------------\n')
        print(self.text_errors)
        return {
            'name':
            'Imported eATAs',
            'type':
            'ir.actions.act_window',
            'res_model':
            'sale.order',
            'view_type':
            'form',
            'view_mode':
            'tree,form',
            'views': [
                (self.env.ref('cci_international.view_ata_tree').id, 'tree'),
                (self.env.ref('cci_international.view_form_delegated').id,
                 'form'),
            ],
            'domain': [('id', 'in', self.new_ids)],
        }
Exemplo n.º 3
0
class MrpWorkorder(models.Model):
    _name = 'mrp.workorder'
    _description = 'Work Order'

    def _read_group_workcenter_id(self, workcenters, domain, order):
        workcenter_ids = self.env.context.get('default_workcenter_id')
        if not workcenter_ids:
            workcenter_ids = workcenters._search(
                [], order=order, access_rights_uid=SUPERUSER_ID)
        return workcenters.browse(workcenter_ids)

    name = fields.Char('Work Order',
                       required=True,
                       states={
                           'done': [('readonly', True)],
                           'cancel': [('readonly', True)]
                       })
    workcenter_id = fields.Many2one('mrp.workcenter',
                                    'Work Center',
                                    required=True,
                                    states={
                                        'done': [('readonly', True)],
                                        'cancel': [('readonly', True)],
                                        'progress': [('readonly', True)]
                                    },
                                    group_expand='_read_group_workcenter_id',
                                    check_company=True)
    working_state = fields.Selection(string='Workcenter Status',
                                     related='workcenter_id.working_state',
                                     readonly=False,
                                     help='Technical: used in views only')
    product_id = fields.Many2one(related='production_id.product_id',
                                 readonly=True,
                                 store=True,
                                 check_company=True)
    product_tracking = fields.Selection(related="product_id.tracking")
    product_uom_id = fields.Many2one('uom.uom',
                                     'Unit of Measure',
                                     required=True,
                                     readonly=True)
    use_create_components_lots = fields.Boolean(
        related="production_id.picking_type_id.use_create_components_lots")
    production_id = fields.Many2one('mrp.production',
                                    'Manufacturing Order',
                                    required=True,
                                    check_company=True,
                                    readonly=True)
    production_availability = fields.Selection(
        string='Stock Availability',
        readonly=True,
        related='production_id.reservation_state',
        store=True,
        help='Technical: used in views and domains only.')
    production_state = fields.Selection(string='Production State',
                                        readonly=True,
                                        related='production_id.state',
                                        help='Technical: used in views only.')
    production_bom_id = fields.Many2one('mrp.bom',
                                        related='production_id.bom_id')
    qty_production = fields.Float('Original Production Quantity',
                                  readonly=True,
                                  related='production_id.product_qty')
    company_id = fields.Many2one(related='production_id.company_id')
    qty_producing = fields.Float(compute='_compute_qty_producing',
                                 inverse='_set_qty_producing',
                                 string='Currently Produced Quantity',
                                 digits='Product Unit of Measure')
    qty_remaining = fields.Float('Quantity To Be Produced',
                                 compute='_compute_qty_remaining',
                                 digits='Product Unit of Measure')
    qty_produced = fields.Float(
        'Quantity',
        default=0.0,
        readonly=True,
        digits='Product Unit of Measure',
        copy=False,
        help="The number of products already handled by this work order")
    is_produced = fields.Boolean(string="Has Been Produced",
                                 compute='_compute_is_produced')
    state = fields.Selection([('pending', 'Waiting for another WO'),
                              ('ready', 'Ready'), ('progress', 'In Progress'),
                              ('done', 'Finished'), ('cancel', 'Cancelled')],
                             string='Status',
                             default='pending',
                             copy=False,
                             readonly=True)
    leave_id = fields.Many2one(
        'resource.calendar.leaves',
        help='Slot into workcenter calendar once planned',
        check_company=True,
        copy=False)
    date_planned_start = fields.Datetime('Scheduled Start Date',
                                         compute='_compute_dates_planned',
                                         inverse='_set_dates_planned',
                                         states={
                                             'done': [('readonly', True)],
                                             'cancel': [('readonly', True)]
                                         },
                                         store=True,
                                         tracking=True,
                                         copy=False)
    date_planned_finished = fields.Datetime('Scheduled End Date',
                                            compute='_compute_dates_planned',
                                            inverse='_set_dates_planned',
                                            states={
                                                'done': [('readonly', True)],
                                                'cancel': [('readonly', True)]
                                            },
                                            store=True,
                                            tracking=True,
                                            copy=False)
    date_start = fields.Datetime('Start Date',
                                 copy=False,
                                 states={
                                     'done': [('readonly', True)],
                                     'cancel': [('readonly', True)]
                                 })
    date_finished = fields.Datetime('End Date',
                                    copy=False,
                                    states={
                                        'done': [('readonly', True)],
                                        'cancel': [('readonly', True)]
                                    })

    duration_expected = fields.Float('Expected Duration',
                                     digits=(16, 2),
                                     default=60.0,
                                     states={
                                         'done': [('readonly', True)],
                                         'cancel': [('readonly', True)]
                                     },
                                     help="Expected duration (in minutes)")
    duration = fields.Float('Real Duration',
                            compute='_compute_duration',
                            inverse='_set_duration',
                            readonly=False,
                            store=True)
    duration_unit = fields.Float('Duration Per Unit',
                                 compute='_compute_duration',
                                 readonly=True,
                                 store=True)
    duration_percent = fields.Integer('Duration Deviation (%)',
                                      compute='_compute_duration',
                                      group_operator="avg",
                                      readonly=True,
                                      store=True)
    progress = fields.Float('Progress Done (%)',
                            digits=(16, 2),
                            compute='_compute_progress')

    operation_id = fields.Many2one('mrp.routing.workcenter',
                                   'Operation',
                                   check_company=True)
    # Should be used differently as BoM can change in the meantime
    worksheet = fields.Binary('Worksheet',
                              related='operation_id.worksheet',
                              readonly=True)
    worksheet_type = fields.Selection(string='Worksheet Type',
                                      related='operation_id.worksheet_type',
                                      readonly=True)
    worksheet_google_slide = fields.Char(
        'Worksheet URL',
        related='operation_id.worksheet_google_slide',
        readonly=True)
    operation_note = fields.Text("Description",
                                 related='operation_id.note',
                                 readonly=True)
    move_raw_ids = fields.One2many('stock.move',
                                   'workorder_id',
                                   'Raw Moves',
                                   domain=[('raw_material_production_id', '!=',
                                            False),
                                           ('production_id', '=', False)])
    move_finished_ids = fields.One2many('stock.move',
                                        'workorder_id',
                                        'Finished Moves',
                                        domain=[('raw_material_production_id',
                                                 '=', False),
                                                ('production_id', '!=', False)
                                                ])
    move_line_ids = fields.One2many(
        'stock.move.line',
        'workorder_id',
        'Moves to Track',
        help=
        "Inventory moves for which you must scan a lot number at this work order"
    )
    finished_lot_id = fields.Many2one(
        'stock.production.lot',
        string='Lot/Serial Number',
        compute='_compute_finished_lot_id',
        inverse='_set_finished_lot_id',
        domain=
        "[('product_id', '=', product_id), ('company_id', '=', company_id)]",
        check_company=True)
    time_ids = fields.One2many('mrp.workcenter.productivity',
                               'workorder_id',
                               copy=False)
    is_user_working = fields.Boolean(
        'Is the Current User Working',
        compute='_compute_working_users',
        help="Technical field indicating whether the current user is working. "
    )
    working_user_ids = fields.One2many(
        'res.users',
        string='Working user on this work order.',
        compute='_compute_working_users')
    last_working_user_id = fields.One2many(
        'res.users',
        string='Last user that worked on this work order.',
        compute='_compute_working_users')

    next_work_order_id = fields.Many2one('mrp.workorder',
                                         "Next Work Order",
                                         check_company=True)
    scrap_ids = fields.One2many('stock.scrap', 'workorder_id')
    scrap_count = fields.Integer(compute='_compute_scrap_move_count',
                                 string='Scrap Move')
    production_date = fields.Datetime(
        'Production Date',
        related='production_id.date_planned_start',
        store=True,
        readonly=False)
    json_popover = fields.Char('Popover Data JSON',
                               compute='_compute_json_popover')
    show_json_popover = fields.Boolean('Show Popover?',
                                       compute='_compute_json_popover')
    consumption = fields.Selection(
        [('strict', 'Strict'), ('warning', 'Warning'),
         ('flexible', 'Flexible')],
        required=True,
    )

    @api.depends('production_state', 'date_planned_start',
                 'date_planned_finished')
    def _compute_json_popover(self):
        previous_wo_data = self.env['mrp.workorder'].read_group(
            [('next_work_order_id', 'in', self.ids)], [
                'ids:array_agg(id)', 'date_planned_start:max',
                'date_planned_finished:max'
            ], ['next_work_order_id'])
        previous_wo_dict = dict([(x['next_work_order_id'][0], {
            'id':
            x['ids'][0],
            'date_planned_start':
            x['date_planned_start'],
            'date_planned_finished':
            x['date_planned_finished']
        }) for x in previous_wo_data])
        if self.ids:
            conflicted_dict = self._get_conflicted_workorder_ids()
        for wo in self:
            infos = []
            if not wo.date_planned_start or not wo.date_planned_finished or not wo.ids:
                wo.show_json_popover = False
                wo.json_popover = False
                continue
            if wo.state in ['pending', 'ready']:
                previous_wo = previous_wo_dict.get(wo.id)
                prev_start = previous_wo and previous_wo[
                    'date_planned_start'] or False
                prev_finished = previous_wo and previous_wo[
                    'date_planned_finished'] or False
                if wo.state == 'pending' and prev_start and not (
                        prev_start > wo.date_planned_start):
                    infos.append({
                        'color':
                        'text-primary',
                        'msg':
                        _("Waiting the previous work order, planned from %(start)s to %(end)s",
                          start=format_datetime(self.env,
                                                prev_start,
                                                dt_format=False),
                          end=format_datetime(self.env,
                                              prev_finished,
                                              dt_format=False))
                    })
                if wo.date_planned_finished < fields.Datetime.now():
                    infos.append({
                        'color':
                        'text-warning',
                        'msg':
                        _("The work order should have already been processed.")
                    })
                if prev_start and prev_start > wo.date_planned_start:
                    infos.append({
                        'color':
                        'text-danger',
                        'msg':
                        _("Scheduled before the previous work order, planned from %(start)s to %(end)s",
                          start=format_datetime(self.env,
                                                prev_start,
                                                dt_format=False),
                          end=format_datetime(self.env,
                                              prev_finished,
                                              dt_format=False))
                    })
                if conflicted_dict.get(wo.id):
                    infos.append({
                        'color':
                        'text-danger',
                        'msg':
                        _(
                            "Planned at the same time than other workorder(s) at %s",
                            wo.workcenter_id.display_name)
                    })
            color_icon = infos and infos[-1]['color'] or False
            wo.show_json_popover = bool(color_icon)
            wo.json_popover = json.dumps({
                'infos':
                infos,
                'color':
                color_icon,
                'icon':
                'fa-exclamation-triangle' if color_icon
                in ['text-warning', 'text-danger'] else 'fa-info-circle',
                'replan':
                color_icon not in [False, 'text-primary']
            })

    @api.depends('production_id.lot_producing_id')
    def _compute_finished_lot_id(self):
        for workorder in self:
            workorder.finished_lot_id = workorder.production_id.lot_producing_id

    def _set_finished_lot_id(self):
        for workorder in self:
            workorder.production_id.lot_producing_id = workorder.finished_lot_id

    @api.depends('production_id.qty_producing')
    def _compute_qty_producing(self):
        for workorder in self:
            workorder.qty_producing = workorder.production_id.qty_producing

    def _set_qty_producing(self):
        for workorder in self:
            if workorder.qty_producing != 0 and workorder.production_id.qty_producing != workorder.qty_producing:
                workorder.production_id.qty_producing = workorder.qty_producing
                workorder.production_id._set_qty_producing()

    # Both `date_planned_start` and `date_planned_finished` are related fields on `leave_id`. Let's say
    # we slide a workorder on a gantt view, a single call to write is made with both
    # fields Changes. As the ORM doesn't batch the write on related fields and instead
    # makes multiple call, the constraint check_dates() is raised.
    # That's why the compute and set methods are needed. to ensure the dates are updated
    # in the same time.
    @api.depends('leave_id')
    def _compute_dates_planned(self):
        for workorder in self:
            workorder.date_planned_start = workorder.leave_id.date_from
            workorder.date_planned_finished = workorder.leave_id.date_to

    def _set_dates_planned(self):
        date_from = self[0].date_planned_start
        date_to = self[0].date_planned_finished
        self.mapped('leave_id').write({
            'date_from': date_from,
            'date_to': date_to,
        })

    def name_get(self):
        res = []
        for wo in self:
            if len(wo.production_id.workorder_ids) == 1:
                res.append(
                    (wo.id, "%s - %s - %s" %
                     (wo.production_id.name, wo.product_id.name, wo.name)))
            else:
                res.append(
                    (wo.id, "%s - %s - %s - %s" %
                     (wo.production_id.workorder_ids.ids.index(wo._origin.id) +
                      1, wo.production_id.name, wo.product_id.name, wo.name)))
        return res

    def unlink(self):
        # Removes references to workorder to avoid Validation Error
        (self.mapped('move_raw_ids') | self.mapped('move_finished_ids')).write(
            {'workorder_id': False})
        self.mapped('leave_id').unlink()
        mo_dirty = self.production_id.filtered(
            lambda mo: mo.state in ("confirmed", "progress", "to_close"))
        res = super().unlink()
        # We need to go through `_action_confirm` for all workorders of the current productions to
        # make sure the links between them are correct (`next_work_order_id` could be obsolete now).
        mo_dirty.workorder_ids._action_confirm()
        return res

    @api.depends('production_id.product_qty', 'qty_produced',
                 'production_id.product_uom_id')
    def _compute_is_produced(self):
        self.is_produced = False
        for order in self.filtered(
                lambda p: p.production_id and p.production_id.product_uom_id):
            rounding = order.production_id.product_uom_id.rounding
            order.is_produced = float_compare(order.qty_produced,
                                              order.production_id.product_qty,
                                              precision_rounding=rounding) >= 0

    @api.depends('time_ids.duration', 'qty_produced')
    def _compute_duration(self):
        for order in self:
            order.duration = sum(order.time_ids.mapped('duration'))
            order.duration_unit = round(order.duration /
                                        max(order.qty_produced, 1),
                                        2)  # rounding 2 because it is a time
            if order.duration_expected:
                order.duration_percent = 100 * (
                    order.duration_expected -
                    order.duration) / order.duration_expected
            else:
                order.duration_percent = 0

    def _set_duration(self):
        def _float_duration_to_second(duration):
            minutes = duration // 1
            seconds = (duration % 1) * 60
            return minutes * 60 + seconds

        for order in self:
            old_order_duation = sum(order.time_ids.mapped('duration'))
            new_order_duration = order.duration
            if new_order_duration == old_order_duation:
                continue

            delta_duration = new_order_duration - old_order_duation

            if delta_duration > 0:
                date_start = datetime.now() - timedelta(
                    seconds=_float_duration_to_second(delta_duration))
                self.env['mrp.workcenter.productivity'].create(
                    order._prepare_timeline_vals(delta_duration, date_start,
                                                 datetime.now()))
            else:
                duration_to_remove = abs(delta_duration)
                timelines = order.time_ids.sorted(lambda t: t.date_start)
                timelines_to_unlink = self.env['mrp.workcenter.productivity']
                for timeline in timelines:
                    if duration_to_remove <= 0.0:
                        break
                    if timeline.duration <= duration_to_remove:
                        duration_to_remove -= timeline.duration
                        timelines_to_unlink |= timeline
                    else:
                        new_time_line_duration = timeline.duration - duration_to_remove
                        timeline.date_start = timeline.date_end - timedelta(
                            seconds=_float_duration_to_second(
                                new_time_line_duration))
                        break
                timelines_to_unlink.unlink()

    @api.depends('duration', 'duration_expected', 'state')
    def _compute_progress(self):
        for order in self:
            if order.state == 'done':
                order.progress = 100
            elif order.duration_expected:
                order.progress = order.duration * 100 / order.duration_expected
            else:
                order.progress = 0

    def _compute_working_users(self):
        """ Checks whether the current user is working, all the users currently working and the last user that worked. """
        for order in self:
            order.working_user_ids = [
                (4, order.id) for order in order.time_ids.filtered(
                    lambda time: not time.date_end).sorted(
                        'date_start').mapped('user_id')
            ]
            if order.working_user_ids:
                order.last_working_user_id = order.working_user_ids[-1]
            elif order.time_ids:
                order.last_working_user_id = order.time_ids.sorted(
                    'date_end')[-1].user_id
            else:
                order.last_working_user_id = False
            if order.time_ids.filtered(
                    lambda x: (x.user_id.id == self.env.user.id) and
                (not x.date_end) and (x.loss_type in
                                      ('productive', 'performance'))):
                order.is_user_working = True
            else:
                order.is_user_working = False

    def _compute_scrap_move_count(self):
        data = self.env['stock.scrap'].read_group(
            [('workorder_id', 'in', self.ids)], ['workorder_id'],
            ['workorder_id'])
        count_data = dict((item['workorder_id'][0], item['workorder_id_count'])
                          for item in data)
        for workorder in self:
            workorder.scrap_count = count_data.get(workorder.id, 0)

    @api.onchange('date_planned_finished')
    def _onchange_date_planned_finished(self):
        if self.date_planned_start and self.date_planned_finished:
            interval = self.workcenter_id.resource_calendar_id.get_work_duration_data(
                self.date_planned_start,
                self.date_planned_finished,
                domain=[('time_type', 'in', ['leave', 'other'])])
            self.duration_expected = interval['hours'] * 60

    @api.onchange('operation_id')
    def _onchange_operation_id(self):
        if self.operation_id:
            self.name = self.operation_id.name
            self.workcenter_id = self.operation_id.workcenter_id.id

    @api.onchange('date_planned_start', 'duration_expected')
    def _onchange_date_planned_start(self):
        if self.date_planned_start and self.duration_expected:
            self.date_planned_finished = self.workcenter_id.resource_calendar_id.plan_hours(
                self.duration_expected / 60.0,
                self.date_planned_start,
                compute_leaves=True,
                domain=[('time_type', 'in', ['leave', 'other'])])

    @api.onchange('operation_id', 'workcenter_id', 'qty_production')
    def _onchange_expected_duration(self):
        self.duration_expected = self._get_duration_expected()

    def write(self, values):
        if 'production_id' in values:
            raise UserError(
                _('You cannot link this work order to another manufacturing order.'
                  ))
        if 'workcenter_id' in values:
            for workorder in self:
                if workorder.workcenter_id.id != values['workcenter_id']:
                    if workorder.state in ('progress', 'done', 'cancel'):
                        raise UserError(
                            _('You cannot change the workcenter of a work order that is in progress or done.'
                              ))
                    workorder.leave_id.resource_id = self.env[
                        'mrp.workcenter'].browse(
                            values['workcenter_id']).resource_id
        if 'date_planned_start' in values or 'date_planned_finished' in values:
            for workorder in self:
                start_date = fields.Datetime.to_datetime(
                    values.get(
                        'date_planned_start')) or workorder.date_planned_start
                end_date = fields.Datetime.to_datetime(
                    values.get('date_planned_finished')
                ) or workorder.date_planned_finished
                if start_date and end_date and start_date > end_date:
                    raise UserError(
                        _('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.'
                          ))
                # Update MO dates if the start date of the first WO or the
                # finished date of the last WO is update.
                if workorder == workorder.production_id.workorder_ids[
                        0] and 'date_planned_start' in values:
                    if values['date_planned_start']:
                        workorder.production_id.with_context(
                            force_date=True).write({
                                'date_planned_start':
                                fields.Datetime.to_datetime(
                                    values['date_planned_start'])
                            })
                if workorder == workorder.production_id.workorder_ids[
                        -1] and 'date_planned_finished' in values:
                    if values['date_planned_finished']:
                        workorder.production_id.with_context(
                            force_date=True).write({
                                'date_planned_finished':
                                fields.Datetime.to_datetime(
                                    values['date_planned_finished'])
                            })
        return super(MrpWorkorder, self).write(values)

    @api.model_create_multi
    def create(self, values):
        res = super().create(values)
        # Auto-confirm manually added workorders.
        # We need to go through `_action_confirm` for all workorders of the current productions to
        # make sure the links between them are correct.
        to_confirm = res.filtered(lambda wo: wo.production_id.state in
                                  ("confirmed", "progress", "to_close"))
        to_confirm = to_confirm.production_id.workorder_ids
        to_confirm._action_confirm()
        return res

    def _action_confirm(self):
        workorders_by_production = defaultdict(
            lambda: self.env['mrp.workorder'])
        for workorder in self:
            workorders_by_production[workorder.production_id] |= workorder

        for production, workorders in workorders_by_production.items():
            workorders_by_bom = defaultdict(lambda: self.env['mrp.workorder'])
            bom = self.env['mrp.bom']
            moves = production.move_raw_ids | production.move_finished_ids

            for workorder in self:
                if workorder.operation_id.bom_id:
                    bom = workorder.operation_id.bom_id
                if not bom:
                    bom = workorder.production_id.bom_id
                previous_workorder = workorders_by_bom[bom][-1:]
                previous_workorder.next_work_order_id = workorder.id
                workorders_by_bom[bom] |= workorder

                moves.filtered(
                    lambda m: m.operation_id == workorder.operation_id).write(
                        {'workorder_id': workorder.id})

            exploded_boms, dummy = production.bom_id.explode(
                production.product_id,
                1,
                picking_type=production.bom_id.picking_type_id)
            exploded_boms = {b[0]: b[1] for b in exploded_boms}
            for move in moves:
                if move.workorder_id:
                    continue
                bom = move.bom_line_id.bom_id
                while bom and bom not in workorders_by_bom:
                    bom_data = exploded_boms.get(bom, {})
                    bom = bom_data.get('parent_line') and bom_data[
                        'parent_line'].bom_id or False
                if bom in workorders_by_bom:
                    move.write(
                        {'workorder_id': workorders_by_bom[bom][-1:].id})
                else:
                    move.write({
                        'workorder_id':
                        workorders_by_bom[production.bom_id][-1:].id
                    })

            for workorders in workorders_by_bom.values():
                if workorders[0].state == 'pending':
                    workorders[0].state = 'ready'
                for workorder in workorders:
                    workorder._start_nextworkorder()

    def _get_byproduct_move_to_update(self):
        return self.production_id.move_finished_ids.filtered(
            lambda x: (x.product_id.id != self.production_id.product_id.id) and
            (x.state not in ('done', 'cancel')))

    def _start_nextworkorder(self):
        if self.state == 'done' and self.next_work_order_id.state == 'pending':
            self.next_work_order_id.state = 'ready'

    @api.model
    def gantt_unavailability(self,
                             start_date,
                             end_date,
                             scale,
                             group_bys=None,
                             rows=None):
        """Get unavailabilities data to display in the Gantt view."""
        workcenter_ids = set()

        def traverse_inplace(func, row, **kargs):
            res = func(row, **kargs)
            if res:
                kargs.update(res)
            for row in row.get('rows'):
                traverse_inplace(func, row, **kargs)

        def search_workcenter_ids(row):
            if row.get('groupedBy') and row.get(
                    'groupedBy')[0] == 'workcenter_id' and row.get('resId'):
                workcenter_ids.add(row.get('resId'))

        for row in rows:
            traverse_inplace(search_workcenter_ids, row)
        start_datetime = fields.Datetime.to_datetime(start_date)
        end_datetime = fields.Datetime.to_datetime(end_date)
        workcenters = self.env['mrp.workcenter'].browse(workcenter_ids)
        unavailability_mapping = workcenters._get_unavailability_intervals(
            start_datetime, end_datetime)

        # Only notable interval (more than one case) is send to the front-end (avoid sending useless information)
        cell_dt = (scale in ['day', 'week'] and timedelta(hours=1)) or (
            scale == 'month' and timedelta(days=1)) or timedelta(days=28)

        def add_unavailability(row, workcenter_id=None):
            if row.get('groupedBy') and row.get(
                    'groupedBy')[0] == 'workcenter_id' and row.get('resId'):
                workcenter_id = row.get('resId')
            if workcenter_id:
                notable_intervals = filter(
                    lambda interval: interval[1] - interval[0] >= cell_dt,
                    unavailability_mapping[workcenter_id])
                row['unavailabilities'] = [{
                    'start': interval[0],
                    'stop': interval[1]
                } for interval in notable_intervals]
                return {'workcenter_id': workcenter_id}

        for row in rows:
            traverse_inplace(add_unavailability, row)
        return rows

    def button_start(self):
        self.ensure_one()
        # As button_start is automatically called in the new view
        if self.state in ('done', 'cancel'):
            return True

        if self.product_tracking == 'serial':
            self.qty_producing = 1.0

        self.env['mrp.workcenter.productivity'].create(
            self._prepare_timeline_vals(self.duration, datetime.now()))
        if self.production_id.state != 'progress':
            self.production_id.write({
                'date_start': datetime.now(),
            })
        if self.state == 'progress':
            return True
        start_date = datetime.now()
        vals = {
            'state': 'progress',
            'date_start': start_date,
        }
        if not self.leave_id:
            leave = self.env['resource.calendar.leaves'].create({
                'name':
                self.display_name,
                'calendar_id':
                self.workcenter_id.resource_calendar_id.id,
                'date_from':
                start_date,
                'date_to':
                start_date + relativedelta(minutes=self.duration_expected),
                'resource_id':
                self.workcenter_id.resource_id.id,
                'time_type':
                'other'
            })
            vals['leave_id'] = leave.id
            return self.write(vals)
        else:
            if self.date_planned_start > start_date:
                vals['date_planned_start'] = start_date
            if self.date_planned_finished and self.date_planned_finished < start_date:
                vals['date_planned_finished'] = start_date
            return self.write(vals)

    def button_finish(self):
        end_date = datetime.now()
        for workorder in self:
            if workorder.state in ('done', 'cancel'):
                continue
            workorder.end_all()
            vals = {
                'state': 'done',
                'date_finished': end_date,
                'date_planned_finished': end_date
            }
            if not workorder.date_start:
                vals['date_start'] = end_date
            if not workorder.date_planned_start or end_date < workorder.date_planned_start:
                vals['date_planned_start'] = end_date
            workorder.write(vals)

            workorder._start_nextworkorder()
        return True

    def end_previous(self, doall=False):
        """
        @param: doall:  This will close all open time lines on the open work orders when doall = True, otherwise
        only the one of the current user
        """
        # TDE CLEANME
        timeline_obj = self.env['mrp.workcenter.productivity']
        domain = [('workorder_id', 'in', self.ids), ('date_end', '=', False)]
        if not doall:
            domain.append(('user_id', '=', self.env.user.id))
        not_productive_timelines = timeline_obj.browse()
        for timeline in timeline_obj.search(domain,
                                            limit=None if doall else 1):
            wo = timeline.workorder_id
            if wo.duration_expected <= wo.duration:
                if timeline.loss_type == 'productive':
                    not_productive_timelines += timeline
                timeline.write({'date_end': fields.Datetime.now()})
            else:
                maxdate = fields.Datetime.from_string(
                    timeline.date_start) + relativedelta(
                        minutes=wo.duration_expected - wo.duration)
                enddate = datetime.now()
                if maxdate > enddate:
                    timeline.write({'date_end': enddate})
                else:
                    timeline.write({'date_end': maxdate})
                    not_productive_timelines += timeline.copy({
                        'date_start':
                        maxdate,
                        'date_end':
                        enddate
                    })
        if not_productive_timelines:
            loss_id = self.env['mrp.workcenter.productivity.loss'].search(
                [('loss_type', '=', 'performance')], limit=1)
            if not len(loss_id):
                raise UserError(
                    _("You need to define at least one unactive productivity loss in the category 'Performance'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses."
                      ))
            not_productive_timelines.write({'loss_id': loss_id.id})
        return True

    def end_all(self):
        return self.end_previous(doall=True)

    def button_pending(self):
        self.end_previous()
        return True

    def button_unblock(self):
        for order in self:
            order.workcenter_id.unblock()
        return True

    def action_cancel(self):
        self.leave_id.unlink()
        return self.write({'state': 'cancel'})

    def action_replan(self):
        """Replan a work order.

        It actually replans every  "ready" or "pending"
        work orders of the linked manufacturing orders.
        """
        for production in self.production_id:
            production._plan_workorders(replan=True)
        return True

    def button_done(self):
        if any(x.state in ('done', 'cancel') for x in self):
            raise UserError(
                _('A Manufacturing Order is already done or cancelled.'))
        self.end_all()
        end_date = datetime.now()
        return self.write({
            'state': 'done',
            'date_finished': end_date,
            'date_planned_finished': end_date,
        })

    def button_scrap(self):
        self.ensure_one()
        return {
            'name': _('Scrap'),
            'view_mode': 'form',
            'res_model': 'stock.scrap',
            'view_id': self.env.ref('stock.stock_scrap_form_view2').id,
            'type': 'ir.actions.act_window',
            'context': {
                'default_company_id':
                self.production_id.company_id.id,
                'default_workorder_id':
                self.id,
                'default_production_id':
                self.production_id.id,
                'product_ids':
                (self.production_id.move_raw_ids.filtered(
                    lambda x: x.state not in ('done', 'cancel'))
                 | self.production_id.move_finished_ids.filtered(
                     lambda x: x.state == 'done')).mapped('product_id').ids
            },
            'target': 'new',
        }

    def action_see_move_scrap(self):
        self.ensure_one()
        action = self.env["ir.actions.actions"]._for_xml_id(
            "stock.action_stock_scrap")
        action['domain'] = [('workorder_id', '=', self.id)]
        return action

    def action_open_wizard(self):
        self.ensure_one()
        action = self.env["ir.actions.actions"]._for_xml_id(
            "mrp.mrp_workorder_mrp_production_form")
        action['res_id'] = self.id
        return action

    @api.depends('qty_production', 'qty_produced')
    def _compute_qty_remaining(self):
        for wo in self:
            wo.qty_remaining = float_round(
                wo.qty_production - wo.qty_produced,
                precision_rounding=wo.production_id.product_uom_id.rounding)

    def _get_duration_expected(self, alternative_workcenter=False, ratio=1):
        self.ensure_one()
        if not self.workcenter_id:
            return self.duration_expected
        if not self.operation_id:
            duration_expected_working = (
                self.duration_expected - self.workcenter_id.time_start -
                self.workcenter_id.time_stop
            ) * self.workcenter_id.time_efficiency / 100.0
            if duration_expected_working < 0:
                duration_expected_working = 0
            return self.workcenter_id.time_start + self.workcenter_id.time_stop + duration_expected_working * ratio * 100.0 / self.workcenter_id.time_efficiency
        qty_production = self.production_id.product_uom_id._compute_quantity(
            self.qty_production, self.production_id.product_id.uom_id)
        cycle_number = float_round(qty_production /
                                   self.workcenter_id.capacity,
                                   precision_digits=0,
                                   rounding_method='UP')
        if alternative_workcenter:
            # TODO : find a better alternative : the settings of workcenter can change
            duration_expected_working = (
                self.duration_expected - self.workcenter_id.time_start -
                self.workcenter_id.time_stop
            ) * self.workcenter_id.time_efficiency / (100.0 * cycle_number)
            if duration_expected_working < 0:
                duration_expected_working = 0
            return alternative_workcenter.time_start + alternative_workcenter.time_stop + cycle_number * duration_expected_working * 100.0 / alternative_workcenter.time_efficiency
        time_cycle = self.operation_id and self.operation_id.time_cycle or 60.0
        return self.workcenter_id.time_start + self.workcenter_id.time_stop + cycle_number * time_cycle * 100.0 / self.workcenter_id.time_efficiency

    def _get_conflicted_workorder_ids(self):
        """Get conlicted workorder(s) with self.

        Conflict means having two workorders in the same time in the same workcenter.

        :return: defaultdict with key as workorder id of self and value as related conflicted workorder
        """
        self.flush([
            'state', 'date_planned_start', 'date_planned_finished',
            'workcenter_id'
        ])
        sql = """
            SELECT wo1.id, wo2.id
            FROM mrp_workorder wo1, mrp_workorder wo2
            WHERE
                wo1.id IN %s
                AND wo1.state IN ('pending','ready')
                AND wo2.state IN ('pending','ready')
                AND wo1.id != wo2.id
                AND wo1.workcenter_id = wo2.workcenter_id
                AND (DATE_TRUNC('second', wo2.date_planned_start), DATE_TRUNC('second', wo2.date_planned_finished))
                    OVERLAPS (DATE_TRUNC('second', wo1.date_planned_start), DATE_TRUNC('second', wo1.date_planned_finished))
        """
        self.env.cr.execute(sql, [tuple(self.ids)])
        res = defaultdict(list)
        for wo1, wo2 in self.env.cr.fetchall():
            res[wo1].append(wo2)
        return res

    @api.model
    def _prepare_component_quantity(self, move, qty_producing):
        """ helper that computes quantity to consume (or to create in case of byproduct)
        depending on the quantity producing and the move's unit factor"""
        if move.product_id.tracking == 'serial':
            uom = move.product_id.uom_id
        else:
            uom = move.product_uom
        return move.product_uom._compute_quantity(qty_producing *
                                                  move.unit_factor,
                                                  uom,
                                                  round=False)

    def _prepare_timeline_vals(self, duration, date_start, date_end=False):
        # Need a loss in case of the real time exceeding the expected
        if not self.duration_expected or duration < self.duration_expected:
            loss_id = self.env['mrp.workcenter.productivity.loss'].search(
                [('loss_type', '=', 'productive')], limit=1)
            if not len(loss_id):
                raise UserError(
                    _("You need to define at least one productivity loss in the category 'Productivity'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses."
                      ))
        else:
            loss_id = self.env['mrp.workcenter.productivity.loss'].search(
                [('loss_type', '=', 'performance')], limit=1)
            if not len(loss_id):
                raise UserError(
                    _("You need to define at least one productivity loss in the category 'Performance'. Create one from the Manufacturing app, menu: Configuration / Productivity Losses."
                      ))
        return {
            'workorder_id': self.id,
            'workcenter_id': self.workcenter_id.id,
            'description': _('Time Tracking: %(user)s',
                             user=self.env.user.name),
            'loss_id': loss_id[0].id,
            'date_start': date_start,
            'date_end': date_end,
            'user_id':
            self.env.user.id,  # FIXME sle: can be inconsistent with company_id
            'company_id': self.company_id.id,
        }

    def _update_finished_move(self):
        """ Update the finished move & move lines in order to set the finished
        product lot on it as well as the produced quantity. This method get the
        information either from the last workorder or from the Produce wizard."""
        production_move = self.production_id.move_finished_ids.filtered(
            lambda move: move.product_id == self.product_id and move.state
            not in ('done', 'cancel'))
        if production_move and production_move.product_id.tracking != 'none':
            if not self.finished_lot_id:
                raise UserError(
                    _('You need to provide a lot for the finished product.'))
            move_line = production_move.move_line_ids.filtered(
                lambda line: line.lot_id.id == self.finished_lot_id.id)
            if move_line:
                if self.product_id.tracking == 'serial':
                    raise UserError(
                        _('You cannot produce the same serial number twice.'))
                move_line.product_uom_qty += self.qty_producing
                move_line.qty_done += self.qty_producing
            else:
                location_dest_id = production_move.location_dest_id._get_putaway_strategy(
                    self.product_id).id or production_move.location_dest_id.id
                move_line.create({
                    'move_id': production_move.id,
                    'product_id': production_move.product_id.id,
                    'lot_id': self.finished_lot_id.id,
                    'product_uom_qty': self.qty_producing,
                    'product_uom_id': self.product_uom_id.id,
                    'qty_done': self.qty_producing,
                    'location_id': production_move.location_id.id,
                    'location_dest_id': location_dest_id,
                })
        else:
            rounding = production_move.product_uom.rounding
            production_move._set_quantity_done(
                float_round(self.qty_producing, precision_rounding=rounding))

    def _check_sn_uniqueness(self):
        """ Alert the user if the serial number as already been produced """
        if self.product_tracking == 'serial' and self.finished_lot_id:
            sml = self.env['stock.move.line'].search_count([
                ('lot_id', '=', self.finished_lot_id.id),
                ('location_id.usage', '=', 'production'), ('qty_done', '=', 1),
                ('state', '=', 'done')
            ])
            if sml:
                raise UserError(
                    _(
                        'This serial number for product %s has already been produced',
                        self.product_id.name))

    def _update_qty_producing(self, quantity):
        self.ensure_one()
        if self.qty_producing:
            self.qty_producing = quantity
Exemplo n.º 4
0
class OeHealthPatient(models.Model):
    _name = 'oeh.medical.patient'
    _description = 'Patient Management'

    _inherits = {
        'res.partner': 'partner_id',
    }

    MARITAL_STATUS = [
        ('Single', 'Single'),
        ('Married', 'Married'),
        ('Widowed', 'Widowed'),
        ('Divorced', 'Divorced'),
        ('Separated', 'Separated'),
    ]

    SEX = [
        ('Male', 'Male'),
        ('Female', 'Female'),
    ]

    BLOOD_TYPE = [
        ('A', 'A'),
        ('B', 'B'),
        ('AB', 'AB'),
        ('O', 'O'),
    ]

    RH = [
        ('+', '+'),
        ('-', '-'),
    ]

    @api.multi
    def _app_count(self):
        oe_apps = self.env['oeh.medical.appointment']
        for pa in self:
            domain = [('patient', '=', pa.id)]
            app_ids = oe_apps.search(domain)
            apps = oe_apps.browse(app_ids)
            app_count = 0
            for ap in apps:
                app_count += 1
            pa.app_count = app_count
        return True

    @api.multi
    def _prescription_count(self):
        oe_pres = self.env['oeh.medical.prescription']
        for pa in self:
            domain = [('patient', '=', pa.id)]
            pres_ids = oe_pres.search(domain)
            pres = oe_pres.browse(pres_ids)
            pres_count = 0
            for pr in pres:
                pres_count += 1
            pa.prescription_count = pres_count
        return True

    @api.multi
    def _admission_count(self):
        oe_admission = self.env['oeh.medical.inpatient']
        for adm in self:
            domain = [('patient', '=', adm.id)]
            admission_ids = oe_admission.search(domain)
            admissions = oe_admission.browse(admission_ids)
            admission_count = 0
            for ad in admissions:
                admission_count += 1
            adm.admission_count = admission_count
        return True

    @api.multi
    def _vaccine_count(self):
        oe_vac = self.env['oeh.medical.vaccines']
        for va in self:
            domain = [('patient', '=', va.id)]
            vec_ids = oe_vac.search(domain)
            vecs = oe_vac.browse(vec_ids)
            vecs_count = 0
            for vac in vecs:
                vecs_count += 1
            va.vaccine_count = vecs_count
        return True

    @api.multi
    def _invoice_count(self):
        oe_invoice = self.env['account.invoice']
        for inv in self:
            invoice_ids = self.env['account.invoice'].search([('patient', '=',
                                                               inv.id)])
            invoices = oe_invoice.browse(invoice_ids)
            invoice_count = 0
            for inv_id in invoices:
                invoice_count += 1
            inv.invoice_count = invoice_count
        return True

    @api.multi
    def _patient_age(self):
        def compute_age_from_dates(patient_dob, patient_deceased, patient_dod):
            now = datetime.datetime.now()
            if (patient_dob):
                dob = datetime.datetime.strptime(
                    patient_dob.strftime('%Y-%m-%d'), '%Y-%m-%d')
                if patient_deceased:
                    dod = datetime.datetime.strptime(
                        patient_dod.strftime('%Y-%m-%d'), '%Y-%m-%d')
                    delta = dod - dob
                    deceased = " (deceased)"
                    years_months_days = str(
                        delta.days // 365) + " years " + str(
                            delta.days % 365) + " days" + deceased
                else:
                    delta = now - dob
                    years_months_days = str(
                        delta.days // 365) + " years " + str(
                            delta.days % 365) + " days"
            else:
                years_months_days = "No DoB !"

            return years_months_days

        for patient_data in self:
            patient_data.age = compute_age_from_dates(patient_data.dob,
                                                      patient_data.deceased,
                                                      patient_data.dod)
        return True

    partner_id = fields.Many2one('res.partner',
                                 string='Related Partner',
                                 required=True,
                                 ondelete='cascade',
                                 help='Partner-related data of the patient')
    family = fields.One2many('oeh.medical.patient.family',
                             'patient_id',
                             string='Family')
    ssn = fields.Char(size=256, string='SSN')
    current_insurance = fields.Many2one(
        'oeh.medical.insurance',
        string="Insurance",
        domain="[('patient','=', active_id),('state','=','Active')]",
        help=
        "Insurance information. You may choose from the different insurances belonging to the patient"
    )
    doctor = fields.Many2one(
        'oeh.medical.physician',
        string='Family Physician',
        help="Current primary care physician / family doctor",
        domain=[('is_pharmacist', '=', False)])
    dob = fields.Date(string='Date of Birth')
    age = fields.Char(
        compute=_patient_age,
        size=32,
        string='Patient Age',
        help=
        "It shows the age of the patient in years(y), months(m) and days(d).\nIf the patient has died, the age shown is the age at time of death, the age corresponding to the date on the death certificate. It will show also \"deceased\" on the field"
    )
    sex = fields.Selection(SEX, string='Sex', index=True)
    marital_status = fields.Selection(MARITAL_STATUS, string='Marital Status')
    blood_type = fields.Selection(BLOOD_TYPE, string='Blood Type')
    rh = fields.Selection(RH, string='Rh')
    identification_code = fields.Char(
        string='Patient ID',
        size=256,
        help='Patient Identifier provided by the Health Center',
        readonly=True)
    ethnic_group = fields.Many2one('oeh.medical.ethnicity', 'Ethnic group')
    critical_info = fields.Text(
        string='Important disease, allergy or procedures information',
        help=
        "Write any important information on the patient's disease, surgeries, allergies, ..."
    )
    general_info = fields.Text(string='General Information',
                               help="General information about the patient")
    genetic_risks = fields.Many2many('oeh.medical.genetics',
                                     'oeh_genetic_risks_rel',
                                     'patient_id',
                                     'genetic_risk_id',
                                     string='Genetic Risks')
    deceased = fields.Boolean(string='Patient Deceased ?',
                              help="Mark if the patient has died")
    dod = fields.Date(string='Date of Death')
    cod = fields.Many2one('oeh.medical.pathology', string='Cause of Death')
    app_count = fields.Integer(compute=_app_count, string="Appointments")
    prescription_count = fields.Integer(compute=_prescription_count,
                                        string="Prescriptions")
    admission_count = fields.Integer(compute=_admission_count,
                                     string="Admission / Discharge")
    vaccine_count = fields.Integer(compute=_vaccine_count, string="Vaccines")
    invoice_count = fields.Integer(compute=_invoice_count, string="Invoices")
    oeh_patient_user_id = fields.Many2one('res.users',
                                          string='Responsible Odoo User')
    prescription_line = fields.One2many('oeh.medical.prescription.line',
                                        'patient',
                                        string='Medicines',
                                        readonly=True)

    _sql_constraints = [(
        'code_oeh_patient_userid_uniq', 'unique (oeh_patient_user_id)',
        "Selected 'Responsible' user is already assigned to another patient !")
                        ]

    @api.model
    def create(self, vals):
        sequence = self.env['ir.sequence'].next_by_code('oeh.medical.patient')
        vals['identification_code'] = sequence
        vals['is_patient'] = True
        health_patient = super(OeHealthPatient, self).create(vals)
        return health_patient

    @api.onchange('state_id')
    def onchange_state_id(self):
        if self.state_id:
            self.country_id = self.state_id.country_id.id

    @api.multi
    def print_patient_label(self):
        return self.env.ref(
            'oehealth.action_report_patient_label').report_action(self)
Exemplo n.º 5
0
class HrEmployeePrivate(models.Model):
    """
    NB: Any field only available on the model hr.employee (i.e. not on the
    hr.employee.public model) should have `groups="hr.group_hr_user"` on its
    definition to avoid being prefetched when the user hasn't access to the
    hr.employee model. Indeed, the prefetch loads the data for all the fields
    that are available according to the group defined on them.
    """
    _name = "hr.employee"
    _description = "Employee"
    _order = 'name'
    _inherit = [
        'hr.employee.base', 'mail.thread', 'mail.activity.mixin',
        'resource.mixin', 'image.mixin'
    ]
    _mail_post_access = 'read'
    _rec_name = 'name'

    @api.model
    def _default_image(self):
        image_path = get_module_resource('hr', 'static/src/img',
                                         'default_image.png')
        return base64.b64encode(open(image_path, 'rb').read())

    cin = fields.Char(string='CIN')
    passport = fields.Char(string='PASSPORT')
    visa_du = fields.Date(string='Visa Du ')
    visa_ou = fields.Date(string='Visa Ou ')

    # resource and user
    # required on the resource, make sure required="True" set in the view
    name = fields.Char(string="Employee Name",
                       related='resource_id.name',
                       store=True,
                       readonly=False,
                       tracking=True)
    user_id = fields.Many2one('res.users',
                              'User',
                              related='resource_id.user_id',
                              store=True,
                              readonly=False)
    user_partner_id = fields.Many2one(related='user_id.partner_id',
                                      related_sudo=False,
                                      string="User's partner")
    active = fields.Boolean('Active',
                            related='resource_id.active',
                            default=True,
                            store=True,
                            readonly=False)
    # private partner
    address_home_id = fields.Many2one(
        'res.partner',
        'Address',
        help=
        'Enter here the private address of the employee, not the one linked to your company.',
        groups="hr.group_hr_user",
        tracking=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    is_address_home_a_company = fields.Boolean(
        'The employee address has a company linked',
        compute='_compute_is_address_home_a_company',
    )
    private_email = fields.Char(related='address_home_id.email',
                                string="Private Email",
                                groups="hr.group_hr_user")
    country_id = fields.Many2one('res.country',
                                 'Nationality (Country)',
                                 groups="hr.group_hr_user",
                                 tracking=True)
    gender = fields.Selection([('male', 'Male'), ('female', 'Female'),
                               ('other', 'Other')],
                              groups="hr.group_hr_user",
                              default="male",
                              tracking=True)
    marital = fields.Selection([('single', 'Single'), ('married', 'Married'),
                                ('cohabitant', 'Legal Cohabitant'),
                                ('widower', 'Widower'),
                                ('divorced', 'Divorced')],
                               string='Marital Status',
                               groups="hr.group_hr_user",
                               default='single',
                               tracking=True)
    spouse_complete_name = fields.Char(string="Spouse Complete Name",
                                       groups="hr.group_hr_user",
                                       tracking=True)
    spouse_birthdate = fields.Date(string="Spouse Birthdate",
                                   groups="hr.group_hr_user",
                                   tracking=True)
    children = fields.Integer(string='Number of Children',
                              groups="hr.group_hr_user",
                              tracking=True)
    place_of_birth = fields.Char('Place of Birth',
                                 groups="hr.group_hr_user",
                                 tracking=True)
    country_of_birth = fields.Many2one('res.country',
                                       string="Country of Birth",
                                       groups="hr.group_hr_user",
                                       tracking=True)
    birthday = fields.Date('Date of Birth',
                           groups="hr.group_hr_user",
                           tracking=True)
    ssnid = fields.Char('SSN No',
                        help='Social Security Number',
                        groups="hr.group_hr_user",
                        tracking=True)
    sinid = fields.Char('SIN No',
                        help='Social Insurance Number',
                        groups="hr.group_hr_user",
                        tracking=True)
    identification_id = fields.Char(string='Identification No',
                                    groups="hr.group_hr_user",
                                    tracking=True)
    passport_id = fields.Char('Passport No',
                              groups="hr.group_hr_user",
                              tracking=True)
    bank_account_id = fields.Many2one(
        'res.partner.bank',
        'Bank Account Number',
        domain=
        "[('partner_id', '=', address_home_id), '|', ('company_id', '=', False), ('company_id', '=', company_id)]",
        groups="hr.group_hr_user",
        tracking=True,
        help='Employee bank salary account')
    permit_no = fields.Char('Work Permit No',
                            groups="hr.group_hr_user",
                            tracking=True)
    visa_no = fields.Char('Visa No', groups="hr.group_hr_user", tracking=True)
    visa_expire = fields.Date('Visa Expire Date',
                              groups="hr.group_hr_user",
                              tracking=True)
    additional_note = fields.Text(string='Additional Note',
                                  groups="hr.group_hr_user",
                                  tracking=True)
    certificate = fields.Selection([
        ('bachelor', 'Bachelor'),
        ('master', 'Master'),
        ('other', 'Other'),
    ],
                                   'Certificate Level',
                                   default='other',
                                   groups="hr.group_hr_user",
                                   tracking=True)
    study_field = fields.Char("Field of Study",
                              groups="hr.group_hr_user",
                              tracking=True)
    study_school = fields.Char("School",
                               groups="hr.group_hr_user",
                               tracking=True)
    emergency_contact = fields.Char("Emergency Contact",
                                    groups="hr.group_hr_user",
                                    tracking=True)
    emergency_phone = fields.Char("Emergency Phone",
                                  groups="hr.group_hr_user",
                                  tracking=True)
    km_home_work = fields.Integer(string="Km Home-Work",
                                  groups="hr.group_hr_user",
                                  tracking=True)

    image_1920 = fields.Image(default=_default_image)
    phone = fields.Char(related='address_home_id.phone',
                        related_sudo=False,
                        readonly=False,
                        string="Private Phone",
                        groups="hr.group_hr_user")
    # employee in company
    child_ids = fields.One2many('hr.employee',
                                'parent_id',
                                string='Direct subordinates')
    category_ids = fields.Many2many('hr.employee.category',
                                    'employee_category_rel',
                                    'emp_id',
                                    'category_id',
                                    groups="hr.group_hr_manager",
                                    string='Tags')
    # misc
    notes = fields.Text('Notes', groups="hr.group_hr_user")
    color = fields.Integer('Color Index', default=0, groups="hr.group_hr_user")
    barcode = fields.Char(string="Badge ID",
                          help="ID used for employee identification.",
                          groups="hr.group_hr_user",
                          copy=False)
    pin = fields.Char(
        string="PIN",
        groups="hr.group_hr_user",
        copy=False,
        help=
        "PIN used to Check In/Out in Kiosk Mode (if enabled in Configuration)."
    )
    departure_reason = fields.Selection([('fired', 'Fired'),
                                         ('resigned', 'Resigned'),
                                         ('retired', 'Retired')],
                                        string="Departure Reason",
                                        groups="hr.group_hr_user",
                                        copy=False,
                                        tracking=True)
    departure_description = fields.Text(string="Additional Information",
                                        groups="hr.group_hr_user",
                                        copy=False,
                                        tracking=True)
    message_main_attachment_id = fields.Many2one(groups="hr.group_hr_user")

    _sql_constraints = [
        ('barcode_uniq', 'unique (barcode)',
         "The Badge ID must be unique, this one is already assigned to another employee."
         ),
        ('user_uniq', 'unique (user_id, company_id)',
         "A user cannot be linked to multiple employees in the same company.")
    ]

    def name_get(self):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self).name_get()
        return self.env['hr.employee.public'].browse(self.ids).name_get()

    def _read(self, fields):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self)._read(fields)

        res = self.env['hr.employee.public'].browse(self.ids).read(fields)
        for r in res:
            record = self.browse(r['id'])
            record._update_cache({k: v
                                  for k, v in r.items() if k in fields},
                                 validate=False)

    def read(self, fields, load='_classic_read'):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self).read(fields, load=load)
        private_fields = set(fields).difference(
            self.env['hr.employee.public']._fields.keys())
        if private_fields:
            raise AccessError(
                _('The fields "%s" you try to read is not available on the public employee profile.'
                  ) % (','.join(private_fields)))
        return self.env['hr.employee.public'].browse(self.ids).read(fields,
                                                                    load=load)

    @api.model
    def load_views(self, views, options=None):
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate, self).load_views(views,
                                                             options=options)
        return self.env['hr.employee.public'].load_views(views,
                                                         options=options)

    @api.model
    def _search(self,
                args,
                offset=0,
                limit=None,
                order=None,
                count=False,
                access_rights_uid=None):
        """
            We override the _search because it is the method that checks the access rights
            This is correct to override the _search. That way we enforce the fact that calling
            search on an hr.employee returns a hr.employee recordset, even if you don't have access
            to this model, as the result of _search (the ids of the public employees) is to be
            browsed on the hr.employee model. This can be trusted as the ids of the public
            employees exactly match the ids of the related hr.employee.
        """
        if self.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate,
                         self)._search(args,
                                       offset=offset,
                                       limit=limit,
                                       order=order,
                                       count=count,
                                       access_rights_uid=access_rights_uid)
        return self.env['hr.employee.public']._search(
            args,
            offset=offset,
            limit=limit,
            order=order,
            count=count,
            access_rights_uid=access_rights_uid)

    def get_formview_id(self, access_uid=None):
        """ Override this method in order to redirect many2one towards the right model depending on access_uid """
        if access_uid:
            self_sudo = self.with_user(access_uid)
        else:
            self_sudo = self

        if self_sudo.check_access_rights('read', raise_exception=False):
            return super(HrEmployeePrivate,
                         self).get_formview_id(access_uid=access_uid)
        # Hardcode the form view for public employee
        return self.env.ref('hr.hr_employee_public_view_form').id

    def get_formview_action(self, access_uid=None):
        """ Override this method in order to redirect many2one towards the right model depending on access_uid """
        res = super(HrEmployeePrivate,
                    self).get_formview_action(access_uid=access_uid)
        if access_uid:
            self_sudo = self.with_user(access_uid)
        else:
            self_sudo = self

        if not self_sudo.check_access_rights('read', raise_exception=False):
            res['res_model'] = 'hr.employee.public'

        return res

    @api.constrains('pin')
    def _verify_pin(self):
        for employee in self:
            if employee.pin and not employee.pin.isdigit():
                raise ValidationError(
                    _("The PIN must be a sequence of digits."))

    @api.onchange('job_id')
    def _onchange_job_id(self):
        if self.job_id:
            self.job_title = self.job_id.name

    @api.onchange('address_id')
    def _onchange_address(self):
        self.work_phone = self.address_id.phone
        self.mobile_phone = self.address_id.mobile

    @api.onchange('company_id')
    def _onchange_company(self):
        address = self.company_id.partner_id.address_get(['default'])
        self.address_id = address['default'] if address else False

    @api.onchange('department_id')
    def _onchange_department(self):
        if self.department_id.manager_id:
            self.parent_id = self.department_id.manager_id

    @api.onchange('user_id')
    def _onchange_user(self):
        if self.user_id:
            self.update(self._sync_user(self.user_id))
            if not self.name:
                self.name = self.user_id.name

    @api.onchange('resource_calendar_id')
    def _onchange_timezone(self):
        if self.resource_calendar_id and not self.tz:
            self.tz = self.resource_calendar_id.tz

    def _sync_user(self, user):
        vals = dict(
            image_1920=user.image_1920,
            work_email=user.email,
            user_id=user.id,
        )
        if user.tz:
            vals['tz'] = user.tz
        return vals

    @api.model
    def create(self, vals):
        if vals.get('user_id'):
            user = self.env['res.users'].browse(vals['user_id'])
            vals.update(self._sync_user(user))
            vals['name'] = vals.get('name', user.name)
        employee = super(HrEmployeePrivate, self).create(vals)
        url = '/web#%s' % url_encode(
            {
                'action': 'hr.plan_wizard_action',
                'active_id': employee.id,
                'active_model': 'hr.employee',
                'menu_id': self.env.ref('hr.menu_hr_root').id,
            })
        employee._message_log(body=_(
            '<b>Congratulations!</b> May I recommend you to setup an <a href="%s">onboarding plan?</a>'
        ) % (url))
        if employee.department_id:
            self.env['mail.channel'].sudo().search([
                ('subscription_department_ids', 'in',
                 employee.department_id.id)
            ])._subscribe_users()
        return employee

    def write(self, vals):
        if 'address_home_id' in vals:
            account_id = vals.get('bank_account_id') or self.bank_account_id.id
            if account_id:
                self.env['res.partner.bank'].browse(
                    account_id).partner_id = vals['address_home_id']
        if vals.get('user_id'):
            vals.update(
                self._sync_user(self.env['res.users'].browse(vals['user_id'])))
        res = super(HrEmployeePrivate, self).write(vals)
        if vals.get('department_id') or vals.get('user_id'):
            department_id = vals['department_id'] if vals.get(
                'department_id') else self[:1].department_id.id
            # When added to a department or changing user, subscribe to the channels auto-subscribed by department
            self.env['mail.channel'].sudo().search([
                ('subscription_department_ids', 'in', department_id)
            ])._subscribe_users()
        return res

    def unlink(self):
        resources = self.mapped('resource_id')
        super(HrEmployeePrivate, self).unlink()
        return resources.unlink()

    def toggle_active(self):
        res = super(HrEmployeePrivate, self).toggle_active()
        self.filtered(lambda employee: employee.active).write({
            'departure_reason':
            False,
            'departure_description':
            False,
        })
        if len(self) == 1 and not self.active:
            return {
                'type': 'ir.actions.act_window',
                'name': _('Register Departure'),
                'res_model': 'hr.departure.wizard',
                'view_mode': 'form',
                'target': 'new',
                'context': {
                    'active_id': self.id
                },
                'views': [[False, 'form']]
            }
        return res

    def generate_random_barcode(self):
        for employee in self:
            employee.barcode = '041' + "".join(
                choice(digits) for i in range(9))

    @api.depends('address_home_id.parent_id')
    def _compute_is_address_home_a_company(self):
        """Checks that chosen address (res.partner) is not linked to a company.
        """
        for employee in self:
            try:
                employee.is_address_home_a_company = employee.address_home_id.parent_id.id is not False
            except AccessError:
                employee.is_address_home_a_company = False

    # ---------------------------------------------------------
    # Business Methods
    # ---------------------------------------------------------

    @api.model
    def get_import_templates(self):
        return [{
            'label': _('Import Template for Employees'),
            'template': '/hr/static/xls/hr_employee.xls'
        }]

    def _post_author(self):
        """
        When a user updates his own employee's data, all operations are performed
        by super user. However, tracking messages should not be posted as OdooBot
        but as the actual user.
        This method is used in the overrides of `_message_log` and `message_post`
        to post messages as the correct user.
        """
        real_user = self.env.context.get('binary_field_real_user')
        if self.env.is_superuser() and real_user:
            self = self.with_user(real_user)
        return self

    # ---------------------------------------------------------
    # Messaging
    # ---------------------------------------------------------

    def _message_log(self, **kwargs):
        return super(HrEmployeePrivate,
                     self._post_author())._message_log(**kwargs)

    @api.returns('mail.message', lambda value: value.id)
    def message_post(self, **kwargs):
        return super(HrEmployeePrivate,
                     self._post_author()).message_post(**kwargs)

    def _sms_get_partner_fields(self):
        return ['user_partner_id']

    def _sms_get_number_fields(self):
        return ['mobile_phone']
Exemplo n.º 6
0
class JobPosting(models.Model):
	_name = "job.posting"

	manpower_req = fields.Selection([('in_house','In House'),('site','Client')], string="Manpower Requisition", default="in_house")
	posting_type = fields.Selection([('new','New'),('replacement','Replacement')], string="Job Posting Type", default="new")
	freshers = fields.Boolean('Allow freshers to apply?')

	req_min_exp = fields.Integer(string='Mininum Experience')
	req_max_exp = fields.Integer(string='Maximum Experience')
	ctc_min = fields.Float('Minimum CTC')
	ctc_max = fields.Float('Maximum CTC')

	opening_date = fields.Date('Opening Date')
	closing_date = fields.Date('Closing Date')
	process = fields.Many2one('interview.round','Process')
	skills = fields.One2many('skillset.child','skill','Skills and Competencies')
	replacement_emp = fields.One2many('replacement.child','replacement_id','Replacement Employees')
	jobprocessone2many = fields.One2many('job.process.child','process_id','')
	job_visible = fields.Boolean('Job')
	upload_po = fields.Binary('Upload PO', attachment=True)
	designation = fields.Many2one('hr.job', string="Designation")
	location = fields.Many2one('site.master', string="Site")
	no_of_openings = fields.Integer('No of Openings')
	department_id = fields.Many2one('hr.department', string="Department")
	job_description = fields.Text("Job Description(JD)")
	client_name = fields.Char(string="Client Name")
	client_location = fields.Many2one('site.master', string="Client Location")
	client_city = fields.Char(string="Client City")
	project_name = fields.Char(string="Project Name")
	project_duration = fields.Char('Project Duration')
	no_of_replacement = fields.Integer('No. Of Replacement')
	client_address = fields.Text('Client Address')
	client_mail_id = fields.Char('Client Mail ID')
	hr_access = fields.Boolean('HR Access')
	hr_manager = fields.Many2one('hr.employee','HR Manager',default=lambda self: self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1))
	hr_spoc_id = fields.Many2one('hr.employee','HR Spoc',track_visibility='onchange')
	spoc_user_id = fields.Many2one('res.users','Spoc User')
	status = fields.Selection([('draft','Draft'),('open','Requisition'),('initiate','Pending'),('done','Closed')], string="Status", default="draft")
	initiated = fields.Boolean('initiated')


	@api.multi
	def view_rounds(self):
		if self.process:
			self.write({'job_visible':True})
			search_record = self.env['round.child'].search([('round_id','=',self.process.id)])
			if not search_record:
				raise ValidationError(_("Kindly add rounds against this Process!"))
			if self.jobprocessone2many:
				for i in self.jobprocessone2many:
					i.unlink()
			for each in search_record:
				self.env['job.process.child'].create({'name':each.name,'stage_id':each.stage_id.id,'process_id':self.id})
		return True


	@api.onchange('hr_spoc_id')
	def onchange_hr_spoc_id(self):
		data = {}
		if self.hr_spoc_id:
			data['spoc_user_id'] = self.hr_spoc_id.user_id.id
		else:
			data['spoc_user_id'] = None
		return {'value':data}


	@api.onchange('opening_date')
	def onchange_opening_date(self):
		if self.opening_date:
			date_object = datetime.strptime(self.opening_date, '%Y-%m-%d')
			self.closing_date = date_object + timedelta(days=24)


	def submit_manpower_requisition(self):
		if not self.no_of_openings or self.no_of_openings < 1:
			raise ValidationError("Please enter the number of openings for this request !")
		self.write({'status': 'open'})
		return True
			

	@api.multi
	def initiate_manpower_requisition(self):
		if self.location:
			location = self.location.id if self.location else None
		else:
			location = self.client_location.id if self.client_location else None
		no_of_openings = self.no_of_openings
		manpower_req = self.manpower_req
		designation = self.designation.id if self.designation else None
		opening_date = self.opening_date
		hr_manager = self.hr_manager.id if self.hr_manager else None
		spoc_user_id = self.spoc_user_id.id if self.spoc_user_id else None
		status = self.status
		inv_id = self.env['interview.list'].create(
			{
				'location':location,
				'no_of_openings':no_of_openings,
				'manpower_req':manpower_req,
				'designation':designation,
				'opening_date':opening_date,
				'hr_manager': hr_manager,
				'spoc_user_id': spoc_user_id,
				'status': status,
				'job_posting':self.id
			})
		self.write({'status': 'initiate','initiated':True})
		return True

	@api.multi
	def complete_job(self):
		self.status = 'done'
		return True

	@api.model
	def create(self,vals):
		job_id = super(JobPosting, self).create(vals)
		# location = vals.get('location') if vals.get('location') else vals.get('client_location')
		# no_of_openings = vals.get('no_of_openings')
		# manpower_req = vals.get('manpower_req')
		# designation = vals.get('designation')
		# opening_date = vals.get('opening_date')
		# hr_manager = vals.get('hr_manager')
		# status = vals.get('status')
		if not job_id.skills:
			raise ValidationError(_("Please add skills!!"))
		# if job_id:
		# 	self.env['interview.list'].create({'location':location,
		# 										'no_of_openings':no_of_openings,
		# 										'manpower_req':manpower_req,
		# 										'designation':designation,
		# 										'opening_date':opening_date,
		# 										'job_posting':job_id.id})	
		return job_id
Exemplo n.º 7
0
class OeHealthAppointment(models.Model):
    _name = 'oeh.medical.appointment'
    _description = 'Appointment'
    _inherit = ['mail.thread']

    URGENCY_LEVEL = [
        ('Normal', 'Normal'),
        ('Urgent', 'Urgent'),
        ('Medical Emergency', 'Medical Emergency'),
    ]

    PATIENT_STATUS = [
        ('Ambulatory', 'Ambulatory'),
        ('Outpatient', 'Outpatient'),
        ('Inpatient', 'Inpatient'),
    ]

    APPOINTMENT_STATUS = [
        ('Scheduled', 'Scheduled'),
        ('Completed', 'Completed'),
        ('Invoiced', 'Invoiced'),
    ]

    # Automatically detect logged in physician
    @api.multi
    def _get_physician(self):
        """Return default physician value"""
        therapist_obj = self.env['oeh.medical.physician']
        domain = [('oeh_user_id', '=', self.env.uid)]
        user_ids = therapist_obj.search(domain, limit=1)
        if user_ids:
            return user_ids.id or False
        else:
            return False

    # Calculating Appointment End date
    @api.multi
    def _get_appointment_end(self):
        for apm in self:
            end_date = False
            duration = 1
            if apm.duration:
                duration = apm.duration
            if apm.appointment_date:
                end_date = datetime.datetime.strptime(
                    apm.appointment_date.strftime("%Y-%m-%d %H:%M:%S"),
                    "%Y-%m-%d %H:%M:%S") + timedelta(hours=duration)
            apm.appointment_end = end_date
        return True

    name = fields.Char(string='Appointment #', size=64, default=lambda *a: '/')
    patient = fields.Many2one('oeh.medical.patient',
                              string='Patient',
                              help="Patient Name",
                              required=True,
                              readonly=True,
                              states={'Scheduled': [('readonly', False)]})
    doctor = fields.Many2one('oeh.medical.physician',
                             string='Physician',
                             help="Current primary care / family doctor",
                             domain=[('is_pharmacist', '=', False)],
                             required=True,
                             readonly=True,
                             states={'Scheduled': [('readonly', False)]},
                             default=_get_physician)
    appointment_date = fields.Datetime(
        string='Appointment Date',
        required=True,
        readonly=True,
        states={'Scheduled': [('readonly', False)]},
        default=datetime.datetime.now())
    appointment_end = fields.Datetime(
        compute=_get_appointment_end,
        string='Appointment End Date',
        readonly=True,
        states={'Scheduled': [('readonly', False)]})
    duration = fields.Integer(string='Duration (Hours)',
                              readonly=True,
                              states={'Scheduled': [('readonly', False)]},
                              default=lambda *a: 1)
    institution = fields.Many2one('oeh.medical.health.center',
                                  string='Health Center',
                                  help="Medical Center",
                                  readonly=True,
                                  states={'Scheduled': [('readonly', False)]})
    urgency_level = fields.Selection(
        URGENCY_LEVEL,
        string='Urgency Level',
        readonly=True,
        states={'Scheduled': [('readonly', False)]},
        default=lambda *a: 'Normal')
    comments = fields.Text(string='Comments',
                           readonly=True,
                           states={'Scheduled': [('readonly', False)]})
    patient_status = fields.Selection(
        PATIENT_STATUS,
        string='Patient Status',
        readonly=True,
        states={'Scheduled': [('readonly', False)]},
        default=lambda *a: 'Inpatient')
    state = fields.Selection(APPOINTMENT_STATUS,
                             string='State',
                             readonly=True,
                             default=lambda *a: 'Scheduled')

    _order = "appointment_date desc"

    @api.model
    def create(self, vals):
        if vals.get('doctor') and vals.get('appointment_date'):
            self.check_physician_availability(vals.get('doctor'),
                                              vals.get('appointment_date'))

        sequence = self.env['ir.sequence'].next_by_code(
            'oeh.medical.appointment')
        vals['name'] = sequence
        health_appointment = super(OeHealthAppointment, self).create(vals)
        return health_appointment

    @api.multi
    def check_physician_availability(self, doctor, appointment_date):
        available = False
        DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
        patient_line_obj = self.env['oeh.medical.physician.line']
        need_to_check_availability = False

        query_doctor_availability = _(
            "select appointment_type from oeh_medical_physician where id=%s"
        ) % (doctor)
        self.env.cr.execute(query_doctor_availability)
        val = self.env.cr.fetchone()
        if val and val[0]:
            if val[0] == "On Weekly Schedule":
                need_to_check_availability = True

        #check if doctor is working on selected day of the week
        if need_to_check_availability:
            selected_day = datetime.datetime.strptime(
                appointment_date, DATETIME_FORMAT).strftime('%A')

            if selected_day:
                avail_days = patient_line_obj.search(
                    [('name', '=', str(selected_day)),
                     ('physician_id', '=', doctor)],
                    limit=1)

                if not avail_days:
                    raise UserError(
                        _('Physician is not available on selected day!'))
                else:
                    #get selected day's start and end time

                    phy_start_time = self.get_time_string(
                        avail_days.start_time).split(':')
                    phy_end_time = self.get_time_string(
                        avail_days.end_time).split(':')

                    user_pool = self.env['res.users']
                    user = user_pool.browse(self.env.uid)
                    tz = pytz.timezone(user.partner_id.tz) or pytz.utc

                    # get localized dates
                    appointment_date = pytz.utc.localize(
                        datetime.datetime.strptime(
                            appointment_date, DATETIME_FORMAT)).astimezone(tz)

                    t1 = datetime.time(int(phy_start_time[0]),
                                       int(phy_start_time[1]), 0)
                    t3 = datetime.time(int(phy_end_time[0]),
                                       int(phy_end_time[1]), 0)

                    #get appointment hour and minute
                    t2 = datetime.time(appointment_date.hour,
                                       appointment_date.minute, 0)

                    if not (t2 > t1 and t2 < t3):
                        raise UserError(
                            _('Physician is not available on selected time!'))
                    else:
                        available = True
        return available

    def get_time_string(self, duration):
        result = ''
        currentHours = int(duration // 1)
        currentMinutes = int(round(duration % 1 * 60))
        if (currentHours <= 9):
            currentHours = "0" + str(currentHours)
        if (currentMinutes <= 9):
            currentMinutes = "0" + str(currentMinutes)
        result = str(currentHours) + ":" + str(currentMinutes)
        return result

    @api.multi
    def _default_account(self):
        journal = self.env['account.journal'].search([('type', '=', 'sale')],
                                                     limit=1)
        return journal.default_credit_account_id.id

    def action_appointment_invoice_create(self):
        invoice_obj = self.env["account.invoice"]
        invoice_line_obj = self.env["account.invoice.line"]
        inv_ids = []

        for acc in self:
            # Create Invoice
            if acc.patient:
                curr_invoice = {
                    'partner_id':
                    acc.patient.partner_id.id,
                    'account_id':
                    acc.patient.partner_id.property_account_receivable_id.id,
                    'patient':
                    acc.patient.id,
                    'state':
                    'draft',
                    'type':
                    'out_invoice',
                    'date_invoice':
                    acc.appointment_date.strftime('%Y-%m-%d'),
                    'origin':
                    "Appointment # : " + acc.name,
                    'sequence_number_next_prefix':
                    False
                }

                inv_ids = invoice_obj.create(curr_invoice)
                inv_id = inv_ids.id

                if inv_ids:
                    prd_account_id = self._default_account()
                    # Create Invoice line
                    curr_invoice_line = {
                        'name': "Consultancy invoice for " + acc.name,
                        'price_unit': acc.doctor.consultancy_price,
                        'quantity': 1,
                        'account_id': prd_account_id,
                        'invoice_id': inv_id,
                    }

                    inv_line_ids = invoice_line_obj.create(curr_invoice_line)

                self.write({'state': 'Invoiced'})
        return {
            'domain': "[('id','=', " + str(inv_id) + ")]",
            'name': _('Appointment Invoice'),
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'account.invoice',
            'type': 'ir.actions.act_window'
        }

    @api.multi
    def set_to_completed(self):
        return self.write({'state': 'Completed'})

    @api.multi
    def unlink(self):
        for appointment in self.filtered(
                lambda appointment: appointment.state not in ['Scheduled']):
            raise UserError(
                _('You can not delete an appointment which is not in "Scheduled" state !!'
                  ))
        return super(OeHealthAppointment, self).unlink()
Exemplo n.º 8
0
class product_images(models.Model):

    "Products Image gallery"
    _name = "product.images"
    _description = __doc__
    _table = "product_images"

    @api.model
    def unlink(self):
        local_media_repository = self.env[
            'res.company'].get_local_media_repository()
        if local_media_repository:
            for image in self:
                path = os.path.join(local_media_repository,
                                    image.product_id.default_code, image.name)
                if os.path.isfile(path):
                    os.remove(path)
        return super(product_images, self).unlink()

    @api.model
    def create(self, vals):
        if vals.get('name', False) and not vals.get('extention', False):
            vals['name'], vals['extention'] = os.path.splitext(vals['name'])
        return super(product_images, self).create(vals)

    @api.multi
    def write(self, vals):
        if vals.get('name', False) and not vals.get('extention', False):
            vals['name'], vals['extention'] = os.path.splitext(vals['name'])
        if vals.get('name', False) or vals.get('extention', False):
            local_media_repository = self.env[
                'res.company'].get_local_media_repository()
            if local_media_repository:
                #                 old_images = self.browse(cr, uid, ids, context=context)
                res = []
                for old_image in self:
                    if vals.get('name', False) and (
                            old_image.name != vals['name']) or vals.get(
                                'extention', False) and (old_image.extention !=
                                                         vals['extention']):
                        old_path = os.path.join(
                            local_media_repository,
                            old_image.product_id.default_code,
                            '%s%s' % (old_image.name, old_image.extention))
                        res.append(
                            super(product_images,
                                  self).write(old_image.id, vals))
                        if 'file' in vals:
                            #a new image have been loaded we should remove the old image
                            #TODO it's look like there is something wrong with function field in openerp indeed the preview is always added in the write :(
                            if os.path.isfile(old_path):
                                os.remove(old_path)
                        else:
                            #we have to rename the image on the file system
                            if os.path.isfile(old_path):
                                os.rename(
                                    old_path,
                                    os.path.join(
                                        local_media_repository,
                                        old_image.product_id.default_code,
                                        '%s%s' %
                                        (old_image.name, old_image.extention)))
                return res
        return super(product_images, self).write(vals)

    @api.multi
    def get_image(self):
        product_product_obj = self.env['product.product']
        product_template_obj = self.env['product.template']
        for rec in self:
            each = rec.read([
                'link', 'url', 'name', 'file_db_store', 'product_id',
                'product_t_id', 'name', 'extention'
            ])[0]
            if each['link']:
                (filename, header) = urllib.request.urlretrieve(each['url'])
                f = open(filename, 'rb')
                img = base64.encodestring(f.read())
                f.close()
                rec.file = img
            else:
                local_media_repository = self.env[
                    'res.company'].get_local_media_repository()
                if local_media_repository:
                    if each['product_t_id']:
                        product_id = product_template_obj.browse(
                            each['product_t_id'][0])
                        product_code = product_id.read(['default_code'
                                                        ])[0]['default_code']
                    else:
                        product_id = product_product_obj.browse(
                            each['product_id'][0])
                        product_code = product_id.read(['default_code'
                                                        ])[0]['default_code']
                    full_path = os.path.join(
                        local_media_repository, product_code,
                        '%s%s' % (each['name'], each['extention']))
                    if os.path.exists(full_path):
                        try:
                            f = open(full_path, 'rb')
                            img = base64.encodestring(f.read())
                            f.close()
                        except Exception as e:
                            return False
                    else:
                        return False
                else:
                    img = each['file_db_store']
                    rec.file = img

    @api.multi
    def _get_image(self):
        res = {}
        for each in self:
            res[each] = self.get_image()
        return res

    @api.multi
    def _check_filestore(self, image_filestore):
        '''check if the filestore is created, if not it create it automatically'''
        #         try:
        if not os.path.isdir(image_filestore):
            os.makedirs(image_filestore)
#         except Exception e:
#             raise osv.except_osv(_('Error'), _('The image filestore can not be created, %s'%e))
        return True

    @api.multi
    def _save_file(self, path, filename, b64_file):
        """Save a file encoded in base 64"""
        full_path = os.path.join(path, filename)
        self._check_filestore(path)
        ofile = open(full_path, 'w')
        try:
            ofile.write(base64.decodestring(b64_file))
        finally:
            ofile.close()
        return True

    @api.multi
    def _set_image(self, name, value, arg):
        local_media_repository = self.env[
            'res.company'].get_local_media_repository()
        if local_media_repository:
            #             image = self.browse(cr, uid, id, context=context)
            return self._save_file(
                os.path.join(local_media_repository,
                             self.product_id.default_code),
                '%s%s' % (self.name, self.extention), value)
        return self.write({'file_db_store': value})

    name = fields.Char('Image Title', size=100, required=True)
    extention = fields.Char('file extention', size=6)
    link = fields.Boolean(
        'Link?',
        default=lambda *a: False,
        help=
        "Images can be linked from files on your file system or remote (Preferred)"
    )
    file_db_store = fields.Binary('Image stored in database')
    file = fields.Char(compute=_get_image,
                       inverse=_set_image,
                       type="binary",
                       method=True,
                       filters='*.png,*.jpg,*.gif')
    url = fields.Char('File Location', size=250)
    comments = fields.Text('Comments')
    product_id = fields.Many2one('product.product', 'Product')

    _sql_constraints = [
        ('uniq_name_product_id', 'UNIQUE(product_id, name)',
         _('A product can have only one image with the same name'))
    ]
Exemplo n.º 9
0
class KeychainAccount(models.Model):
    """Manage all accounts of external systems in one place."""

    _name = 'keychain.account'

    name = fields.Char(required=True, help="Humain readable label")
    technical_name = fields.Char(
        required=True,
        help="Technical name. Must be unique")
    namespace = fields.Selection(selection=[],
                                 help="Type of account",
                                 required=True)
    environment = fields.Char(
        required=False,
        help="'prod', 'dev', etc. or empty (for all)"
    )
    login = fields.Char(help="Login")
    clear_password = fields.Char(
        help="Password. Leave empty if no changes",
        inverse='_inverse_set_password',
        compute='_compute_password',
        store=False)
    password = fields.Char(
        help="Password is derived from clear_password",
        readonly=True)
    data = fields.Text(help="Additionnal data as json")

    def _compute_password(self):
        # Only needed in v8 for _description_searchable issues
        return True

    def _get_password(self):
        """Password in clear text."""
        try:
            return self._decode_password(self.password)
        except UserError as warn:
            raise UserError(_(
                "%s \n"
                "Account: %s %s %s " % (
                    warn,
                    self.login, self.name, self.technical_name
                )
            ))

    def get_data(self):
        """Data in dict form."""
        return self._parse_data(self.data)

    @api.constrains('data')
    def _check_data(self):
        """Ensure valid input in data field."""
        for account in self:
            if account.data:
                parsed = account._parse_data(account.data)
                if not account._validate_data(parsed):
                    raise ValidationError(_("Data not valid"))

    def _inverse_set_password(self):
        """Encode password from clear text."""
        # inverse function
        for rec in self:
            rec.password = rec._encode_password(
                rec.clear_password, rec.environment)

    @api.model
    def retrieve(self, domain):
        """Search accounts for a given domain.

        Environment is added by this function.
        Use this instead of search() to benefit from environment filtering.
        Use user.has_group() and suspend_security() before
        calling this method.
        """
        domain.append(['environment', 'in', self._retrieve_env()])
        return self.search(domain)

    @api.multi
    def write(self, vals):
        """At this time there is no namespace set."""
        if not vals.get('data') and not self.data:
            vals['data'] = self._serialize_data(self._init_data())
        return super(KeychainAccount, self).write(vals)

    @implemented_by_keychain
    def _validate_data(self, data):
        """Ensure data is valid according to the namespace.

        How to use:
        - Create a method prefixed with your namespace
        - Put your validation logic inside
        - Return true if data is valid for your usage

        This method will be called on write().
        If false is returned an user error will be raised.

        Example:
        def _hereismynamspace_validate_data():
            return len(data.get('some_param', '') > 6)

        @params data dict
        @returns boolean
        """
        pass

    def _default_validate_data(self, data):
        """Default validation.

        By default says data is always valid.
        See _validata_data() for more information.
        """
        return True

    @implemented_by_keychain
    def _init_data(self):
        """Initialize data field.

        How to use:
        - Create a method prefixed with your namespace
        - Return a dict with the keys and may be default
        values your expect.

        This method will be called on write().

        Example:
        def _hereismynamspace_init_data():
            return { 'some_param': 'default_value' }

        @returns dict
        """
        pass

    def _default_init_data(self):
        """Default initialization.

        See _init_data() for more information.
        """
        return {}

    @staticmethod
    def _retrieve_env():
        """Return the current environments.

        You may override this function to fit your needs.

        returns: a tuple like:
            ('dev', 'test', False)
            Which means accounts for dev, test and blank (not set)
            Order is important: the first one is used for encryption.
        """
        current = config.get('running_env') or False
        envs = [current]
        if False not in envs:
            envs.append(False)
        return envs

    @staticmethod
    def _serialize_data(data):
        return json.dumps(data)

    @staticmethod
    def _parse_data(data):
        try:
            return json.loads(data)
        except ValueError:
            raise ValidationError(_("Data should be a valid JSON"))

    @classmethod
    def _encode_password(cls, data, env):
        cipher = cls._get_cipher(env)
        return cipher.encrypt(str((data or '').encode('UTF-8')))

    @classmethod
    def _decode_password(cls, data):
        cipher = cls._get_cipher()
        try:
            return unicode(cipher.decrypt(str(data)), 'UTF-8')
        except InvalidToken:
            raise UserError(_(
                "Password has been encrypted with a different "
                "key. Unless you can recover the previous key, "
                "this password is unreadable."
            ))

    @classmethod
    def _get_cipher(cls, force_env=None):
        """Return a cipher using the keys of environments.

        force_env = name of the env key.
        Useful for encoding against one precise env
        """
        def _get_keys(envs):
            suffixes = [
                '_%s' % env if env else ''
                for env in envs]  # ('_dev', '')
            keys_name = [
                'keychain_key%s' % suf
                for suf in suffixes]  # prefix it
            keys_str = [
                config.get(key)
                for key in keys_name]  # fetch from config
            return [
                Fernet(key) for key in keys_str  # build Fernet object
                if key and len(key) > 0  # remove False values
            ]

        if force_env:
            envs = [force_env]
        else:
            envs = cls._retrieve_env()  # ex: ('dev', False)
        keys = _get_keys(envs)
        if len(keys) == 0:
            raise UserError(_(
                "No 'keychain_key_%s' entries found in config file. "
                "Use a key similar to: %s" % (envs[0], Fernet.generate_key())
            ))
        return MultiFernet(keys)
Exemplo n.º 10
0
class TmsExpenseLine(models.Model):
    _name = 'tms.expense.line'
    _description = 'Expense Line'

    loan_id = fields.Many2one('tms.expense.loan', string='Loan')
    travel_id = fields.Many2one(
        'tms.travel',
        string='Travel')
    expense_id = fields.Many2one(
        'tms.expense',
        string='Expense',
        readonly=True)
    product_qty = fields.Float(
        string='Qty', default=1.0)
    unit_price = fields.Float()
    price_subtotal = fields.Float(
        compute='_compute_price_subtotal',
        string='Subtotal',)
    product_uom_id = fields.Many2one(
        'product.uom',
        string='Unit of Measure')
    line_type = fields.Selection(
        [('real_expense', 'Real Expense'),
         ('made_up_expense', 'Made-up Expense'),
         ('salary', 'Salary'),
         ('fuel', 'Fuel'),
         ('fuel_cash', 'Fuel in Cash'),
         ('refund', 'Refund'),
         ('salary_retention', 'Salary Retention'),
         ('salary_discount', 'Salary Discount'),
         ('other_income', 'Other Income'),
         ('tollstations', 'Toll Stations'),
         ('loan', 'Loan')],
        compute='_compute_line_type',
        store=True, readonly=True)
    name = fields.Char(
        'Description',
        required=True)
    sequence = fields.Integer(
        help="Gives the sequence order when displaying a list of "
        "sales order lines.",
        default=10)
    price_total = fields.Float(
        string='Total',
        compute='_compute_price_total',
    )
    tax_amount = fields.Float(
        compute='_compute_tax_amount',)
    special_tax_amount = fields.Float(
        string='Special Tax'
    )
    tax_ids = fields.Many2many(
        'account.tax',
        string='Taxes',
        domain=[('type_tax_use', '=', 'purchase')])
    notes = fields.Text()
    employee_id = fields.Many2one(
        'hr.employee',
        string='Driver')
    date = fields.Date()
    state = fields.Char(readonly=True)
    control = fields.Boolean()
    automatic = fields.Boolean(
        help="Check this if you want to create Advances and/or "
        "Fuel Vouchers for this line automatically")
    is_invoice = fields.Boolean(string='Is Invoice?')
    partner_id = fields.Many2one(
        'res.partner', string='Supplier',
        domain=[('company_type', '=', 'company')])
    invoice_date = fields.Date('Date')
    invoice_number = fields.Char()
    invoice_id = fields.Many2one(
        'account.invoice',
        string='Supplier Invoice')
    product_id = fields.Many2one(
        'product.product',
        string='Product',
        required=True,)
    route_id = fields.Many2one(
        'tms.route', related='travel_id.route_id',
        string='Route', readonly=True)

    @api.onchange('product_id')
    def _onchange_product_id(self):
        if self.line_type not in [
                'salary', 'salary_retention', 'salary_discount']:
            self.tax_ids = self.product_id.supplier_taxes_id
        self.line_type = self.product_id.tms_product_category
        self.product_uom_id = self.product_id.uom_id.id
        self.name = self.product_id.name

    @api.depends('product_id')
    def _compute_line_type(self):
        for rec in self:
            rec.line_type = rec.product_id.tms_product_category

    @api.depends('tax_ids', 'product_qty', 'unit_price')
    def _compute_tax_amount(self):
        for rec in self:
            taxes = rec.tax_ids.compute_all(
                rec.unit_price, rec.expense_id.currency_id,
                rec.product_qty,
                rec.expense_id.employee_id.address_home_id)
            if taxes['taxes']:
                for tax in taxes['taxes']:
                    rec.tax_amount += tax['amount']
            else:
                rec.tax_amount = 0.0

    @api.depends('product_qty', 'unit_price', 'line_type')
    def _compute_price_subtotal(self):
        for rec in self:
            if rec.line_type in [
                    'salary_retention', 'salary_discount', 'loan']:
                rec.price_subtotal = rec.product_qty * rec.unit_price * -1
            elif rec.line_type == 'fuel':
                rec.price_subtotal = rec.unit_price
            else:
                rec.price_subtotal = rec.product_qty * rec.unit_price

    @api.depends('price_subtotal', 'tax_ids')
    def _compute_price_total(self):
        for rec in self:
            if rec.line_type == 'fuel':
                rec.price_total = rec.unit_price
            elif rec.line_type in [
                    'salary_retention', 'salary_discount', 'loan']:
                rec.price_total = rec.price_subtotal - rec.tax_amount
            else:
                rec.price_total = rec.price_subtotal + rec.tax_amount

    @api.model
    def create(self, values):
        expense_line = super(TmsExpenseLine, self).create(values)
        if expense_line.line_type in (
                'salary_discount', 'salary_retention', 'loan'):
            if expense_line.price_total > 0:
                raise ValidationError(_('This line type needs a '
                                        'negative value to continue!'))
        return expense_line
Exemplo n.º 11
0
class SQLRequestMixin(models.AbstractModel):
    _name = 'sql.request.mixin'

    _clean_query_enabled = True

    _check_prohibited_words_enabled = True

    _check_execution_enabled = True

    _sql_request_groups_relation = False

    _sql_request_users_relation = False

    STATE_SELECTION = [
        ('draft', 'Draft'),
        ('sql_valid', 'SQL Valid'),
    ]

    PROHIBITED_WORDS = [
        'delete',
        'drop',
        'insert',
        'alter',
        'truncate',
        'execute',
        'create',
        'update',
        'ir_config_parameter',
    ]

    # Default Section
    @api.model
    def _default_group_ids(self):
        ir_model_obj = self.env['ir.model.data']
        return [
            ir_model_obj.xmlid_to_res_id(
                'sql_request_abstract.group_sql_request_user')
        ]

    @api.model
    def _default_user_ids(self):
        return []

    # Columns Section
    name = fields.Char('Name', required=True)

    query = fields.Text(
        string='Query',
        required=True,
        help="You can't use the following words"
        ": DELETE, DROP, CREATE, INSERT, ALTER, TRUNCATE, EXECUTE, UPDATE.")

    state = fields.Selection(
        string='State',
        selection=STATE_SELECTION,
        default='draft',
        help="State of the Request:\n"
        " * 'Draft': Not tested\n"
        " * 'SQL Valid': SQL Request has been checked and is valid")

    group_ids = fields.Many2many(comodel_name='res.groups',
                                 string='Allowed Groups',
                                 relation=_sql_request_groups_relation,
                                 column1='sql_id',
                                 column2='group_id',
                                 default=_default_group_ids)

    user_ids = fields.Many2many(comodel_name='res.users',
                                string='Allowed Users',
                                relation=_sql_request_users_relation,
                                column1='sql_id',
                                column2='user_id',
                                default=_default_user_ids)

    # Action Section
    @api.multi
    def button_validate_sql_expression(self):
        for item in self:
            if item._clean_query_enabled:
                item._clean_query()
            if item._check_prohibited_words_enabled:
                item._check_prohibited_words()
            if item._check_execution_enabled:
                item._check_execution()
            item.state = 'sql_valid'

    @api.multi
    def button_set_draft(self):
        self.write({'state': 'draft'})

    # API Section
    @api.multi
    def _execute_sql_request(self,
                             params=None,
                             mode='fetchall',
                             rollback=True,
                             view_name=False,
                             copy_options="CSV HEADER DELIMITER ';'"):
        """Execute a SQL request on the current database.

        ??? This function checks before if the user has the
        right to execute the request.

        :param params: (dict) of keys / values that will be replaced in
            the sql query, before executing it.
        :param mode: (str) result type expected. Available settings :
            * 'view': create a view with the select query. Extra param
                required 'view_name'.
            * 'materialized_view': create a MATERIALIZED VIEW with the
                select query. Extra parameter required 'view_name'.
            * 'fetchall': execute the select request, and return the
                result of 'cr.fetchall()'.
            * 'fetchone' : execute the select request, and return the
                result of 'cr.fetchone()'
        :param rollback: (boolean) mention if a rollback should be played after
            the execution of the query. Please keep this feature enabled
            for security reason, except if necessary.
            (Ignored if @mode in ('view', 'materialized_view'))
        :param view_name: (str) name of the view.
            (Ignored if @mode not in ('view', 'materialized_view'))
        :param copy_options: (str) mentions extra options for
            "COPY request STDOUT WITH xxx" request.
            (Ignored if @mode != 'stdout')

        ..note:: The following exceptions could be raised:
            psycopg2.ProgrammingError: Error in the SQL Request.
            odoo.exceptions.UserError:
                * 'mode' is not implemented.
                * materialized view is not supported by the Postgresql Server.
        """
        self.ensure_one()
        res = False
        # Check if the request is in a valid state
        if self.state == 'draft':
            raise UserError(
                _("It is not allowed to execute a not checked request."))

        # Disable rollback if a creation of a view is asked
        if mode in ('view', 'materialized_view'):
            rollback = False

        params = params or {}
        # pylint: disable=sql-injection
        query = self.query % params
        query = query.decode('utf-8')

        if mode in ('fetchone', 'fetchall'):
            pass
        elif mode == 'stdout':
            query = "COPY (%s) TO STDOUT WITH %s" % (query, copy_options)
        elif mode in 'view':
            query = "CREATE VIEW %s AS (%s);" % (query, view_name)
        elif mode in 'materialized_view':
            self._check_materialized_view_available()
            query = "CREATE MATERIALIZED VIEW %s AS (%s);" % (query, view_name)
        else:
            raise UserError(_("Unimplemented mode : '%s'" % mode))

        if rollback:
            rollback_name = self._create_savepoint()
        try:
            if mode == 'stdout':
                output = StringIO.StringIO()
                self.env.cr.copy_expert(query, output)
                output.getvalue()
                res = base64.b64encode(output.getvalue())
                output.close()
            else:
                self.env.cr.execute(query)
                if mode == 'fetchall':
                    res = self.env.cr.fetchall()
                elif mode == 'fetchone':
                    res = self.env.cr.fetchone()
        finally:
            self._rollback_savepoint(rollback_name)

        return res

    # Private Section
    @api.model
    def _create_savepoint(self):
        rollback_name = '%s_%s' % (self._name.replace('.',
                                                      '_'), uuid.uuid1().hex)
        # pylint: disable=sql-injection
        req = "SAVEPOINT %s" % (rollback_name)
        self.env.cr.execute(req)
        return rollback_name

    @api.model
    def _rollback_savepoint(self, rollback_name):
        # pylint: disable=sql-injection
        req = "ROLLBACK TO SAVEPOINT %s" % (rollback_name)
        self.env.cr.execute(req)

    @api.model
    def _check_materialized_view_available(self):
        self.env.cr.execute("SHOW server_version;")
        res = self.env.cr.fetchone()[0].split('.')
        minor_version = float('.'.join(res[:2]))
        if minor_version < 9.3:
            raise UserError(
                _("Materialized View requires PostgreSQL 9.3 or greater but"
                  " PostgreSQL %s is currently installed.") % (minor_version))

    @api.multi
    def _clean_query(self):
        self.ensure_one()
        query = self.query.strip()
        while query[-1] == ';':
            query = query[:-1]
        self.query = query

    @api.multi
    def _check_prohibited_words(self):
        """Check if the query contains prohibited words, to avoid maliscious
        SQL requests"""
        self.ensure_one()
        query = self.query.lower()
        for word in self.PROHIBITED_WORDS:
            expr = r'\b%s\b' % word
            is_not_safe = re.search(expr, query)
            if is_not_safe:
                raise UserError(
                    _("The query is not allowed because it contains unsafe word"
                      " '%s'") % (word))

    @api.multi
    def _check_execution(self):
        """Ensure that the query is valid, trying to execute it. A rollback
        is done after."""
        self.ensure_one()
        query = self._prepare_request_check_execution()
        rollback_name = self._create_savepoint()
        res = False
        try:
            self.env.cr.execute(query)
            res = self._hook_executed_request()
        except ProgrammingError as e:
            raise UserError(
                _("The SQL query is not valid:\n\n %s") % e.message)
        finally:
            self._rollback_savepoint(rollback_name)
        return res

    @api.multi
    def _prepare_request_check_execution(self):
        """Overload me to replace some part of the query, if it contains
        parameters"""
        self.ensure_one()
        return self.query

    def _hook_executed_request(self):
        """Overload me to insert custom code, when the SQL request has
        been executed, before the rollback.
        """
        self.ensure_one()
        return False
Exemplo n.º 12
0
class Module(models.Model):
    _name = "ir.module.module"
    _rec_name = "shortdesc"
    _description = "Module"
    _order = 'sequence,name'

    @api.model
    def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
        res = super(Module, self).fields_view_get(view_id, view_type, toolbar=toolbar, submenu=False)
        if view_type == 'form' and res.get('toolbar',False):
            install_id = self.env.ref('base.action_server_module_immediate_install').id
            action = [rec for rec in res['toolbar']['action'] if rec.get('id', False) != install_id]
            res['toolbar'] = {'action': action}
        return res

    @classmethod
    def get_module_info(cls, name):
        try:
            return modules.load_information_from_description_file(name)
        except Exception:
            _logger.debug('Error when trying to fetch information for module %s', name, exc_info=True)
            return {}

    @api.depends('name', 'description')
    def _get_desc(self):
        for module in self:
            path = modules.get_module_resource(module.name, 'static/description/index.html')
            if path:
                with tools.file_open(path, 'rb') as desc_file:
                    doc = desc_file.read()
                    html = lxml.html.document_fromstring(doc)
                    for element, attribute, link, pos in html.iterlinks():
                        if element.get('src') and not '//' in element.get('src') and not 'static/' in element.get('src'):
                            element.set('src', "/%s/static/description/%s" % (module.name, element.get('src')))
                    module.description_html = tools.html_sanitize(lxml.html.tostring(html))
            else:
                overrides = {
                    'embed_stylesheet': False,
                    'doctitle_xform': False,
                    'output_encoding': 'unicode',
                    'xml_declaration': False,
                    'file_insertion_enabled': False,
                }
                output = publish_string(source=module.description if not module.application and module.description else '', settings_overrides=overrides, writer=MyWriter())
                module.description_html = tools.html_sanitize(output)

    @api.depends('name')
    def _get_latest_version(self):
        default_version = modules.adapt_version('1.0')
        for module in self:
            module.installed_version = self.get_module_info(module.name).get('version', default_version)

    @api.depends('name', 'state')
    def _get_views(self):
        IrModelData = self.env['ir.model.data'].with_context(active_test=True)
        dmodels = ['ir.ui.view', 'ir.actions.report', 'ir.ui.menu']

        for module in self:
            # Skip uninstalled modules below, no data to find anyway.
            if module.state not in ('installed', 'to upgrade', 'to remove'):
                module.views_by_module = ""
                module.reports_by_module = ""
                module.menus_by_module = ""
                continue

            # then, search and group ir.model.data records
            imd_models = defaultdict(list)
            imd_domain = [('module', '=', module.name), ('model', 'in', tuple(dmodels))]
            for data in IrModelData.sudo().search(imd_domain):
                imd_models[data.model].append(data.res_id)

            def browse(model):
                # as this method is called before the module update, some xmlid
                # may be invalid at this stage; explictly filter records before
                # reading them
                return self.env[model].browse(imd_models[model]).exists()

            def format_view(v):
                return '%s%s (%s)' % (v.inherit_id and '* INHERIT ' or '', v.name, v.type)

            module.views_by_module = "\n".join(sorted(format_view(v) for v in browse('ir.ui.view')))
            module.reports_by_module = "\n".join(sorted(r.name for r in browse('ir.actions.report')))
            module.menus_by_module = "\n".join(sorted(m.complete_name for m in browse('ir.ui.menu')))

    @api.depends('icon')
    def _get_icon_image(self):
        for module in self:
            module.icon_image = ''
            if module.icon:
                path_parts = module.icon.split('/')
                path = modules.get_module_resource(path_parts[1], *path_parts[2:])
            else:
                path = modules.module.get_module_icon(module.name)
            if path:
                with tools.file_open(path, 'rb') as image_file:
                    module.icon_image = base64.b64encode(image_file.read())

    name = fields.Char('Technical Name', readonly=True, required=True, index=True)
    category_id = fields.Many2one('ir.module.category', string='Category', readonly=True, index=True)
    shortdesc = fields.Char('Module Name', readonly=True, translate=True)
    summary = fields.Char('Summary', readonly=True, translate=True)
    description = fields.Text('Description', readonly=True, translate=True)
    description_html = fields.Html('Description HTML', compute='_get_desc')
    author = fields.Char("Author", readonly=True)
    maintainer = fields.Char('Maintainer', readonly=True)
    contributors = fields.Text('Contributors', readonly=True)
    website = fields.Char("Website", readonly=True)

    # attention: Incorrect field names !!
    #   installed_version refers the latest version (the one on disk)
    #   latest_version refers the installed version (the one in database)
    #   published_version refers the version available on the repository
    installed_version = fields.Char('Latest Version', compute='_get_latest_version')
    latest_version = fields.Char('Installed Version', readonly=True)
    published_version = fields.Char('Published Version', readonly=True)

    url = fields.Char('URL', readonly=True)
    sequence = fields.Integer('Sequence', default=100)
    dependencies_id = fields.One2many('ir.module.module.dependency', 'module_id',
                                       string='Dependencies', readonly=True)
    exclusion_ids = fields.One2many('ir.module.module.exclusion', 'module_id',
                                    string='Exclusions', readonly=True)
    auto_install = fields.Boolean('Automatic Installation',
                                   help='An auto-installable module is automatically installed by the '
                                        'system when all its dependencies are satisfied. '
                                        'If the module has no dependency, it is always installed.')
    state = fields.Selection(STATES, string='Status', default='uninstallable', readonly=True, index=True)
    demo = fields.Boolean('Demo Data', default=False, readonly=True)
    license = fields.Selection([
        ('GPL-2', 'GPL Version 2'),
        ('GPL-2 or any later version', 'GPL-2 or later version'),
        ('GPL-3', 'GPL Version 3'),
        ('GPL-3 or any later version', 'GPL-3 or later version'),
        ('AGPL-3', 'Affero GPL-3'),
        ('LGPL-3', 'LGPL Version 3'),
        ('Other OSI approved licence', 'Other OSI Approved Licence'),
        ('OEEL-1', 'Odoo Enterprise Edition License v1.0'),
        ('OPL-1', 'Odoo Proprietary License v1.0'),
        ('Other proprietary', 'Other Proprietary')
    ], string='License', default='LGPL-3', readonly=True)
    menus_by_module = fields.Text(string='Menus', compute='_get_views', store=True)
    reports_by_module = fields.Text(string='Reports', compute='_get_views', store=True)
    views_by_module = fields.Text(string='Views', compute='_get_views', store=True)
    application = fields.Boolean('Application', readonly=True)
    icon = fields.Char('Icon URL')
    icon_image = fields.Binary(string='Icon', compute='_get_icon_image')
    to_buy = fields.Boolean('Odoo Enterprise Module', default=False)

    _sql_constraints = [
        ('name_uniq', 'UNIQUE (name)', 'The name of the module must be unique!'),
    ]

    @api.multi
    def unlink(self):
        if not self:
            return True
        for module in self:
            if module.state in ('installed', 'to upgrade', 'to remove', 'to install'):
                raise UserError(_('You are trying to remove a module that is installed or will be installed.'))
        self.clear_caches()
        return super(Module, self).unlink()

    @staticmethod
    def _check_external_dependencies(terp):
        depends = terp.get('external_dependencies')
        if not depends:
            return
        for pydep in depends.get('python', []):
            try:
                importlib.import_module(pydep)
            except ImportError:
                raise ImportError('No module named %s' % (pydep,))

        for binary in depends.get('bin', []):
            try:
                tools.find_in_path(binary)
            except IOError:
                raise Exception('Unable to find %r in path' % (binary,))

    @classmethod
    def check_external_dependencies(cls, module_name, newstate='to install'):
        terp = cls.get_module_info(module_name)
        try:
            cls._check_external_dependencies(terp)
        except Exception as e:
            if newstate == 'to install':
                msg = _('Unable to install module "%s" because an external dependency is not met: %s')
            elif newstate == 'to upgrade':
                msg = _('Unable to upgrade module "%s" because an external dependency is not met: %s')
            else:
                msg = _('Unable to process module "%s" because an external dependency is not met: %s')
            raise UserError(msg % (module_name, e.args[0]))

    @api.multi
    def _state_update(self, newstate, states_to_update, level=100):
        if level < 1:
            raise UserError(_('Recursion error in modules dependencies !'))

        # whether some modules are installed with demo data
        demo = False

        for module in self:
            # determine dependency modules to update/others
            update_mods, ready_mods = self.browse(), self.browse()
            for dep in module.dependencies_id:
                if dep.state == 'unknown':
                    raise UserError(_("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,))
                if dep.depend_id.state == newstate:
                    ready_mods += dep.depend_id
                else:
                    update_mods += dep.depend_id

            # update dependency modules that require it, and determine demo for module
            update_demo = update_mods._state_update(newstate, states_to_update, level=level-1)
            module_demo = module.demo or update_demo or any(mod.demo for mod in ready_mods)
            demo = demo or module_demo

            if module.state in states_to_update:
                # check dependencies and update module itself
                self.check_external_dependencies(module.name, newstate)
                module.write({'state': newstate, 'demo': module_demo})

        return demo

    @assert_log_admin_access
    @api.multi
    def button_install(self):
        # domain to select auto-installable (but not yet installed) modules
        auto_domain = [('state', '=', 'uninstalled'), ('auto_install', '=', True)]

        # determine whether an auto-install module must be installed:
        #  - all its dependencies are installed or to be installed,
        #  - at least one dependency is 'to install'
        install_states = frozenset(('installed', 'to install', 'to upgrade'))
        def must_install(module):
            states = set(dep.state for dep in module.dependencies_id)
            return states <= install_states and 'to install' in states

        modules = self
        while modules:
            # Mark the given modules and their dependencies to be installed.
            modules._state_update('to install', ['uninstalled'])

            # Determine which auto-installable modules must be installed.
            modules = self.search(auto_domain).filtered(must_install)

        # the modules that are installed/to install/to upgrade
        install_mods = self.search([('state', 'in', list(install_states))])

        # check individual exclusions
        install_names = {module.name for module in install_mods}
        for module in install_mods:
            for exclusion in module.exclusion_ids:
                if exclusion.name in install_names:
                    msg = _('Modules "%s" and "%s" are incompatible.')
                    raise UserError(msg % (module.shortdesc, exclusion.exclusion_id.shortdesc))

        # check category exclusions
        def closure(module):
            todo = result = module
            while todo:
                result |= todo
                todo = todo.mapped('dependencies_id.depend_id')
            return result

        exclusives = self.env['ir.module.category'].search([('exclusive', '=', True)])
        for category in exclusives:
            # retrieve installed modules in category and sub-categories
            categories = category.search([('id', 'child_of', category.ids)])
            modules = install_mods.filtered(lambda mod: mod.category_id in categories)
            # the installation is valid if all installed modules in categories
            # belong to the transitive dependencies of one of them
            if modules and not any(modules <= closure(module) for module in modules):
                msg = _('You are trying to install incompatible modules in category "%s":')
                labels = dict(self.fields_get(['state'])['state']['selection'])
                raise UserError("\n".join([msg % category.name] + [
                    "- %s (%s)" % (module.shortdesc, labels[module.state])
                    for module in modules
                ]))

        return dict(ACTION_DICT, name=_('Install'))

    @assert_log_admin_access
    @api.multi
    def button_immediate_install(self):
        """ Installs the selected module(s) immediately and fully,
        returns the next res.config action to execute

        :returns: next res.config item to execute
        :rtype: dict[str, object]
        """
        _logger.info('User #%d triggered module installation', self.env.uid)
        return self._button_immediate_function(type(self).button_install)

    @assert_log_admin_access
    @api.multi
    def button_install_cancel(self):
        self.write({'state': 'uninstalled', 'demo': False})
        return True

    @assert_log_admin_access
    @api.multi
    def module_uninstall(self):
        """ Perform the various steps required to uninstall a module completely
        including the deletion of all database structures created by the module:
        tables, columns, constraints, etc.
        """
        modules_to_remove = self.mapped('name')
        self.env['ir.model.data']._module_data_uninstall(modules_to_remove)
        # we deactivate prefetching to not try to read a column that has been deleted
        self.with_context(prefetch_fields=False).write({'state': 'uninstalled', 'latest_version': False})
        return True

    @api.multi
    def _remove_copied_views(self):
        """ Remove the copies of the views installed by the modules in `self`.

        Those copies do not have an external id so they will not be cleaned by
        `_module_data_uninstall`. This is why we rely on `key` instead.

        It is important to remove these copies because using them will crash if
        they rely on data that don't exist anymore if the module is removed.
        """
        domain = expression.OR([[('key', '=like', m.name + '.%')] for m in self])
        orphans = self.env['ir.ui.view'].with_context(**{'active_test': False, MODULE_UNINSTALL_FLAG: True}).search(domain)
        orphans.unlink()

    @api.multi
    @api.returns('self')
    def downstream_dependencies(self, known_deps=None,
                                exclude_states=('uninstalled', 'uninstallable', 'to remove')):
        """ Return the modules that directly or indirectly depend on the modules
        in `self`, and that satisfy the `exclude_states` filter.
        """
        if not self:
            return self
        known_deps = known_deps or self.browse()
        query = """ SELECT DISTINCT m.id
                    FROM ir_module_module_dependency d
                    JOIN ir_module_module m ON (d.module_id=m.id)
                    WHERE
                        d.name IN (SELECT name from ir_module_module where id in %s) AND
                        m.state NOT IN %s AND
                        m.id NOT IN %s """
        self._cr.execute(query, (tuple(self.ids), tuple(exclude_states), tuple(known_deps.ids or self.ids)))
        new_deps = self.browse([row[0] for row in self._cr.fetchall()])
        missing_mods = new_deps - known_deps
        known_deps |= new_deps
        if missing_mods:
            known_deps |= missing_mods.downstream_dependencies(known_deps, exclude_states)
        return known_deps

    @api.multi
    @api.returns('self')
    def upstream_dependencies(self, known_deps=None,
                              exclude_states=('installed', 'uninstallable', 'to remove')):
        """ Return the dependency tree of modules of the modules in `self`, and
        that satisfy the `exclude_states` filter.
        """
        if not self:
            return self
        known_deps = known_deps or self.browse()
        query = """ SELECT DISTINCT m.id
                    FROM ir_module_module_dependency d
                    JOIN ir_module_module m ON (d.module_id=m.id)
                    WHERE
                        m.name IN (SELECT name from ir_module_module_dependency where module_id in %s) AND
                        m.state NOT IN %s AND
                        m.id NOT IN %s """
        self._cr.execute(query, (tuple(self.ids), tuple(exclude_states), tuple(known_deps.ids or self.ids)))
        new_deps = self.browse([row[0] for row in self._cr.fetchall()])
        missing_mods = new_deps - known_deps
        known_deps |= new_deps
        if missing_mods:
            known_deps |= missing_mods.upstream_dependencies(known_deps, exclude_states)
        return known_deps

    def next(self):
        """
        Return the action linked to an ir.actions.todo is there exists one that
        should be executed. Otherwise, redirect to /web
        """
        Todos = self.env['ir.actions.todo']
        _logger.info('getting next %s', Todos)
        active_todo = Todos.search([('state', '=', 'open')], limit=1)
        if active_todo:
            _logger.info('next action is "%s"', active_todo.name)
            return active_todo.action_launch()
        return {
            'type': 'ir.actions.act_url',
            'target': 'self',
            'url': '/web',
        }

    @api.multi
    def _button_immediate_function(self, function):
        try:
            # This is done because the installation/uninstallation/upgrade can modify a currently
            # running cron job and prevent it from finishing, and since the ir_cron table is locked
            # during execution, the lock won't be released until timeout.
            self._cr.execute("SELECT * FROM ir_cron FOR UPDATE NOWAIT")
        except psycopg2.OperationalError:
            raise UserError(_("The server is busy right now, module operations are not possible at"
                              " this time, please try again later."))
        function(self)

        self._cr.commit()
        api.Environment.reset()
        modules.registry.Registry.new(self._cr.dbname, update_module=True)

        self._cr.commit()
        env = api.Environment(self._cr, self._uid, self._context)
        # pylint: disable=next-method-called
        config = env['ir.module.module'].next() or {}
        if config.get('type') not in ('ir.actions.act_window_close',):
            return config

        # reload the client; open the first available root menu
        menu = env['ir.ui.menu'].search([('parent_id', '=', False)])[:1]
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
            'params': {'menu_id': menu.id},
        }

    @assert_log_admin_access
    @api.multi
    def button_immediate_uninstall(self):
        """
        Uninstall the selected module(s) immediately and fully,
        returns the next res.config action to execute
        """
        _logger.info('User #%d triggered module uninstallation', self.env.uid)
        return self._button_immediate_function(type(self).button_uninstall)

    @assert_log_admin_access
    @api.multi
    def button_uninstall(self):
        if 'base' in self.mapped('name'):
            raise UserError(_("The `base` module cannot be uninstalled"))
        if not all(state in ('installed', 'to upgrade') for state in self.mapped('state')):
            raise UserError(_(
                "One or more of the selected modules have already been uninstalled, if you "
                "believe this to be an error, you may try again later or contact support."
            ))
        deps = self.downstream_dependencies()
        (self + deps).write({'state': 'to remove'})
        return dict(ACTION_DICT, name=_('Uninstall'))

    @assert_log_admin_access
    @api.multi
    def button_uninstall_wizard(self):
        """ Launch the wizard to uninstall the given module. """
        return {
            'type': 'ir.actions.act_window',
            'target': 'new',
            'name': _('Uninstall module'),
            'view_mode': 'form',
            'res_model': 'base.module.uninstall',
            'context': {'default_module_id': self.id},
        }

    @api.multi
    def button_uninstall_cancel(self):
        self.write({'state': 'installed'})
        return True

    @assert_log_admin_access
    @api.multi
    def button_immediate_upgrade(self):
        """
        Upgrade the selected module(s) immediately and fully,
        return the next res.config action to execute
        """
        return self._button_immediate_function(type(self).button_upgrade)

    @assert_log_admin_access
    @api.multi
    def button_upgrade(self):
        Dependency = self.env['ir.module.module.dependency']
        self.update_list()

        todo = list(self)
        i = 0
        while i < len(todo):
            module = todo[i]
            i += 1
            if module.state not in ('installed', 'to upgrade'):
                raise UserError(_("Can not upgrade module '%s'. It is not installed.") % (module.name,))
            self.check_external_dependencies(module.name, 'to upgrade')
            for dep in Dependency.search([('name', '=', module.name)]):
                if dep.module_id.state == 'installed' and dep.module_id not in todo:
                    todo.append(dep.module_id)

        self.browse(module.id for module in todo).write({'state': 'to upgrade'})

        to_install = []
        for module in todo:
            for dep in module.dependencies_id:
                if dep.state == 'unknown':
                    raise UserError(_('You try to upgrade the module %s that depends on the module: %s.\nBut this module is not available in your system.') % (module.name, dep.name,))
                if dep.state == 'uninstalled':
                    to_install += self.search([('name', '=', dep.name)]).ids

        self.browse(to_install).button_install()
        return dict(ACTION_DICT, name=_('Apply Schedule Upgrade'))

    @assert_log_admin_access
    @api.multi
    def button_upgrade_cancel(self):
        self.write({'state': 'installed'})
        return True

    @staticmethod
    def get_values_from_terp(terp):
        return {
            'description': terp.get('description', ''),
            'shortdesc': terp.get('name', ''),
            'author': terp.get('author', 'Unknown'),
            'maintainer': terp.get('maintainer', False),
            'contributors': ', '.join(terp.get('contributors', [])) or False,
            'website': terp.get('website', ''),
            'license': terp.get('license', 'LGPL-3'),
            'sequence': terp.get('sequence', 100),
            'application': terp.get('application', False),
            'auto_install': terp.get('auto_install', False),
            'icon': terp.get('icon', False),
            'summary': terp.get('summary', ''),
            'url': terp.get('url') or terp.get('live_test_url', ''),
            'to_buy': False
        }

    @api.model
    def create(self, vals):
        new = super(Module, self).create(vals)
        module_metadata = {
            'name': 'module_%s' % vals['name'],
            'model': 'ir.module.module',
            'module': 'base',
            'res_id': new.id,
            'noupdate': True,
        }
        self.env['ir.model.data'].create(module_metadata)
        return new

    # update the list of available packages
    @assert_log_admin_access
    @api.model
    def update_list(self):
        res = [0, 0]    # [update, add]

        default_version = modules.adapt_version('1.0')
        known_mods = self.with_context(lang=None).search([])
        known_mods_names = {mod.name: mod for mod in known_mods}

        # iterate through detected modules and update/create them in db
        for mod_name in modules.get_modules():
            mod = known_mods_names.get(mod_name)
            terp = self.get_module_info(mod_name)
            values = self.get_values_from_terp(terp)

            if mod:
                updated_values = {}
                for key in values:
                    old = getattr(mod, key)
                    updated = tools.ustr(values[key]) if isinstance(values[key], pycompat.string_types) else values[key]
                    if (old or updated) and updated != old:
                        updated_values[key] = values[key]
                if terp.get('installable', True) and mod.state == 'uninstallable':
                    updated_values['state'] = 'uninstalled'
                if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version):
                    res[0] += 1
                if updated_values:
                    mod.write(updated_values)
            else:
                mod_path = modules.get_module_path(mod_name)
                if not mod_path or not terp:
                    continue
                state = "uninstalled" if terp.get('installable', True) else "uninstallable"
                mod = self.create(dict(name=mod_name, state=state, **values))
                res[1] += 1

            mod._update_dependencies(terp.get('depends', []))
            mod._update_exclusions(terp.get('excludes', []))
            mod._update_category(terp.get('category', 'Uncategorized'))

        return res

    @assert_log_admin_access
    @api.multi
    def download(self, download=True):
        return []

    @assert_log_admin_access
    @api.model
    def install_from_urls(self, urls):
        if not self.env.user.has_group('base.group_system'):
            raise AccessDenied()

        # One-click install is opt-in - cfr Issue #15225
        ad_dir = tools.config.addons_data_dir
        if not os.access(ad_dir, os.W_OK):
            msg = (_("Automatic install of downloaded Apps is currently disabled.") + "\n\n" +
                   _("To enable it, make sure this directory exists and is writable on the server:") +
                   "\n%s" % ad_dir)
            _logger.warning(msg)
            raise UserError(msg)

        apps_server = urls.url_parse(self.get_apps_server())

        OPENERP = odoo.release.product_name.lower()
        tmp = tempfile.mkdtemp()
        _logger.debug('Install from url: %r', urls)
        try:
            # 1. Download & unzip missing modules
            for module_name, url in urls.items():
                if not url:
                    continue    # nothing to download, local version is already the last one

                up = urls.url_parse(url)
                if up.scheme != apps_server.scheme or up.netloc != apps_server.netloc:
                    raise AccessDenied()

                try:
                    _logger.info('Downloading module `%s` from OpenERP Apps', module_name)
                    response = requests.get(url)
                    response.raise_for_status()
                    content = response.content
                except Exception:
                    _logger.exception('Failed to fetch module %s', module_name)
                    raise UserError(_('The `%s` module appears to be unavailable at the moment, please try again later.') % module_name)
                else:
                    zipfile.ZipFile(io.BytesIO(content)).extractall(tmp)
                    assert os.path.isdir(os.path.join(tmp, module_name))

            # 2a. Copy/Replace module source in addons path
            for module_name, url in urls.items():
                if module_name == OPENERP or not url:
                    continue    # OPENERP is special case, handled below, and no URL means local module
                module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False)
                bck = backup(module_path, False)
                _logger.info('Copy downloaded module `%s` to `%s`', module_name, module_path)
                shutil.move(os.path.join(tmp, module_name), module_path)
                if bck:
                    shutil.rmtree(bck)

            # 2b.  Copy/Replace server+base module source if downloaded
            if urls.get(OPENERP):
                # special case. it contains the server and the base module.
                # extract path is not the same
                base_path = os.path.dirname(modules.get_module_path('base'))

                # copy all modules in the SERVER/odoo/addons directory to the new "odoo" module (except base itself)
                for d in os.listdir(base_path):
                    if d != 'base' and os.path.isdir(os.path.join(base_path, d)):
                        destdir = os.path.join(tmp, OPENERP, 'addons', d)    # XXX 'odoo' subdirectory ?
                        shutil.copytree(os.path.join(base_path, d), destdir)

                # then replace the server by the new "base" module
                server_dir = tools.config['root_path']      # XXX or dirname()
                bck = backup(server_dir)
                _logger.info('Copy downloaded module `odoo` to `%s`', server_dir)
                shutil.move(os.path.join(tmp, OPENERP), server_dir)
                #if bck:
                #    shutil.rmtree(bck)

            self.update_list()

            with_urls = [module_name for module_name, url in urls.items() if url]
            downloaded = self.search([('name', 'in', with_urls)])
            installed = self.search([('id', 'in', downloaded.ids), ('state', '=', 'installed')])

            to_install = self.search([('name', 'in', list(urls)), ('state', '=', 'uninstalled')])
            post_install_action = to_install.button_immediate_install()

            if installed or to_install:
                # in this case, force server restart to reload python code...
                self._cr.commit()
                odoo.service.server.restart()
                return {
                    'type': 'ir.actions.client',
                    'tag': 'home',
                    'params': {'wait': True},
                }
            return post_install_action

        finally:
            shutil.rmtree(tmp)

    @api.model
    def get_apps_server(self):
        return tools.config.get('apps_server', 'https://apps.odoo.com/apps')

    def _update_dependencies(self, depends=None):
        existing = set(dep.name for dep in self.dependencies_id)
        needed = set(depends or [])
        for dep in (needed - existing):
            self._cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (self.id, dep))
        for dep in (existing - needed):
            self._cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (self.id, dep))
        self.invalidate_cache(['dependencies_id'], self.ids)

    def _update_exclusions(self, excludes=None):
        existing = set(excl.name for excl in self.exclusion_ids)
        needed = set(excludes or [])
        for name in (needed - existing):
            self._cr.execute('INSERT INTO ir_module_module_exclusion (module_id, name) VALUES (%s, %s)', (self.id, name))
        for name in (existing - needed):
            self._cr.execute('DELETE FROM ir_module_module_exclusion WHERE module_id=%s AND name=%s', (self.id, name))
        self.invalidate_cache(['exclusion_ids'], self.ids)

    def _update_category(self, category='Uncategorized'):
        current_category = self.category_id
        current_category_path = []
        while current_category:
            current_category_path.insert(0, current_category.name)
            current_category = current_category.parent_id

        categs = category.split('/')
        if categs != current_category_path:
            cat_id = modules.db.create_categories(self._cr, categs)
            self.write({'category_id': cat_id})

    @api.multi
    def _update_translations(self, filter_lang=None):
        if not filter_lang:
            langs = self.env['res.lang'].search([('translatable', '=', True)])
            filter_lang = [lang.code for lang in langs]
        elif not isinstance(filter_lang, (list, tuple)):
            filter_lang = [filter_lang]

        update_mods = self.filtered(lambda r: r.state in ('installed', 'to install', 'to upgrade'))
        mod_dict = {
            mod.name: mod.dependencies_id.mapped('name')
            for mod in update_mods
        }
        mod_names = topological_sort(mod_dict)
        self.env['ir.translation'].load_module_terms(mod_names, filter_lang)

    @api.multi
    def _check(self):
        for module in self:
            if not module.description_html:
                _logger.warning('module %s: description is empty !', module.name)

    @api.model
    @tools.ormcache()
    def _installed(self):
        """ Return the set of installed modules as a dictionary {name: id} """
        return {
            module.name: module.id
            for module in self.sudo().search([('state', '=', 'installed')])
        }
Exemplo n.º 13
0
class BackupNotification(models.Model):
    _name = "odoo_backup_sh.notification"
    _description = "Backup Notifications"
    _inherit = ["mail.thread", "mail.activity.mixin"]
    _order = "date_create desc"
    _rec_name = "date_create"

    date_create = fields.Datetime("Date",
                                  readonly=True,
                                  default=fields.Datetime.now)
    type = fields.Selection(
        [
            ("insufficient_credits", "Insufficient Credits"),
            ("forecast_insufficient_credits",
             "Forecast About Insufficient Credits"),
            ("other", "Other"),
        ],
        string="Notification Type",
        readonly=True,
        default="other",
    )
    message = fields.Text("Message", readonly=True)
    is_read = fields.Boolean("Is Read", readonly=True)

    def toggle_is_read(self):
        self.write({"is_read": not self.is_read})
        self.activity_ids.unlink()
        return True

    def create_mail_activity_record(self):
        self.env["mail.activity"].create({
            "res_id":
            self.id,
            "res_model_id":
            self.env.ref(
                "odoo_backup_sh.model_odoo_backup_sh_notification").id,
            "activity_type_id":
            self.env.ref("odoo_backup_sh.mail_activity_data_notification").id,
            "summary":
            "Please read important message.",
            "date_deadline":
            datetime.today().strftime(DEFAULT_SERVER_DATE_FORMAT),
        })

    @api.model
    def fetch_notifications(self):
        config_params = self.env["ir.config_parameter"]
        data = {
            "params": {
                "user_key":
                config_params.get_param("odoo_backup_sh.user_key"),
                "date_last_request":
                config_params.get_param("odoo_backup_sh.date_last_request",
                                        None),
            }
        }
        response = requests.post(BACKUP_SERVICE_ENDPOINT +
                                 "/fetch_notifications",
                                 json=data).json()["result"]
        config_params.set_param("odoo_backup_sh.date_last_request",
                                response["date_last_request"])
        for n in response["notifications"]:
            if n.get("type") == "forecast_insufficient_credits":
                existing_forecast_msg = self.search([
                    ("type", "=", "forecast_insufficient_credits")
                ])
                if existing_forecast_msg:
                    if existing_forecast_msg.activity_ids:
                        existing_forecast_msg.activity_ids.unlink()
                    existing_forecast_msg.unlink()
            new_record = self.create(n)
            new_record.create_mail_activity_record()
Exemplo n.º 14
0
class MuskathlonRegistration(models.Model):
    _name = 'event.registration'
    _inherit = 'event.registration'

    lead_id = fields.Many2one('crm.lead', 'Lead')
    backup_id = fields.Integer(help='Old muskathlon registration id')
    is_muskathlon = fields.Boolean(
        related='compassion_event_id.website_muskathlon')
    sport_discipline_id = fields.Many2one(
        'sport.discipline',
        'Sport discipline',
    )
    sport_level = fields.Selection([('beginner', 'Beginner'),
                                    ('average', 'Average'),
                                    ('advanced', 'Advanced')])
    sport_level_description = fields.Text('Describe your sport experience')
    t_shirt_size = fields.Selection(
        related='partner_id.advocate_details_id.t_shirt_size', store=True)
    t_shirt_type = fields.Selection(
        related='partner_id.advocate_details_id.t_shirt_type', store=True)
    muskathlon_participant_id = fields.Char(
        related='partner_id.muskathlon_participant_id')
    muskathlon_event_id = fields.Char(
        related='compassion_event_id.muskathlon_event_id')
    reg_id = fields.Char(string='Muskathlon registration ID', size=128)

    is_in_two_months = fields.Boolean(compute='_compute_is_in_two_months')

    _sql_constraints = [
        ('reg_unique', 'unique(event_id,partner_id)',
         'Only one registration per participant/event is allowed!')
    ]

    @api.model
    def create(self, values):
        # Automatically complete the task sign_child_protection if the charter
        # has already been signed.
        partner = self.env['res.partner'].browse(values.get('partner_id'))
        completed_tasks = values.setdefault('completed_task_ids', [])
        if partner and partner.has_agreed_child_protection_charter:
            task = self.env.ref('muskathlon.task_sign_child_protection')
            completed_tasks.append((4, task.id))
        if partner and partner.user_ids and any(
                partner.mapped('user_ids.login_date')):
            task = self.env.ref('muskathlon.task_activate_account')
            completed_tasks.append((4, task.id))
        return super(MuskathlonRegistration, self).create(values)

    @api.multi
    def _compute_step2_tasks(self):
        # Consider Muskathlon task for scan passport
        super(MuskathlonRegistration, self)._compute_step2_tasks()
        muskathlon_passport = self.env.ref('muskathlon.task_scan_passport')
        for reg in self:
            reg.passport_uploaded = muskathlon_passport in \
                reg.completed_task_ids

    def _compute_amount_raised(self):
        # Use Muskathlon report to compute Muskathlon event donation
        muskathlon_report = self.env['muskathlon.report']
        m_reg = self.filtered('compassion_event_id.website_muskathlon')
        for registration in m_reg:
            amount_raised = int(
                sum(item.amount for item in muskathlon_report.search([
                    ('user_id', '=', registration.partner_id.id),
                    ('event_id', '=', registration.compassion_event_id.id),
                ])))
            registration.amount_raised = amount_raised
        super(MuskathlonRegistration, (self - m_reg))._compute_amount_raised()

    def _compute_is_in_two_months(self):
        """this function define is the bollean hide or not the survey"""
        for registration in self:
            today = datetime.date.today()
            start_day = fields.Date.from_string(registration.event_begin_date)
            delta = start_day - today
            registration.is_in_two_months = delta.days < 60

    @api.onchange('event_id')
    def onchange_event_id(self):
        return {
            'domain': {
                'sport_discipline_id':
                [('id', 'in',
                  self.compassion_event_id.sport_discipline_ids.ids)]
            }
        }

    @api.onchange('sport_discipline_id')
    def onchange_sport_discipline(self):
        if self.sport_discipline_id and self.sport_discipline_id not in \
                self.compassion_event_id.sport_discipline_ids:
            self.sport_discipline_id = False
            return {
                'warning': {
                    'title': _('Invalid sport'),
                    'message': _('This sport is not in muskathlon')
                }
            }

    @job(default_channel='root.muskathlon')
    @related_action('related_action_registration')
    def notify_new_registration(self):
        """Notify user for registration"""
        partners = self.mapped('user_id.partner_id') | self.event_id.mapped(
            'message_partner_ids')
        self.message_subscribe(partners.ids)
        self.message_post(
            body=_(
                "The participant registered through the Muskathlon website."),
            subject=_("%s - New Muskathlon registration") % self.name,
            message_type='email',
            subtype="website_event_compassion.mt_registration_create")
        return True

    @job(default_channel='root.muskathlon')
    @related_action('related_action_registration')
    def muskathlon_medical_survey_done(self):
        for registration in self:
            user_input = self.env['survey.user_input'].search([
                ('partner_id', '=', registration.partner_id_id),
                ('survey_id', '=', registration.event_id.medical_survey_id.id)
            ],
                                                              limit=1)

            registration.write({
                'completed_task_ids':
                [(4, self.env.ref('muskathlon.task_medical').id)],
                'medical_survey_id':
                user_input.id
            })

            # here we need to send a mail to the muskathlon doctor
            muskathlon_doctor_email = self.env[
                'ir.config_parameter'].get_param('muskathlon.doctor.email')
            if muskathlon_doctor_email:
                template = self.env \
                    .ref("muskathlon.medical_survey_to_doctor_template") \
                    .with_context(email_to=muskathlon_doctor_email).sudo()
                template.send_mail(
                    user_input.id,
                    force_send=True,
                    email_values={'email_to': muskathlon_doctor_email})
        return True
Exemplo n.º 15
0
class Surgery(models.Model):
    _name = 'medical.surgery'
    _description = 'Surgery'
    _inherit = 'res.partner'


    def surgery_duration(self, name):

        if (self.surgery_end_date and self.surgery_date):
            return self.surgery_end_date - self.surgery_date
        else:
            return None

    def patient_age_at_surgery(self, name):
        if (self.patient.name.dob and self.surgery_date):
            rdelta = relativedelta (self.surgery_date.date(),
                self.patient.name.dob)
            years_months_days = str(rdelta.years) + 'y ' \
                + str(rdelta.months) + 'm ' \
                + str(rdelta.days) + 'd'
            return years_months_days
        else:
            return None

    patient = fields.Many2one(
        comodel_name='medical.patient', 
        string='Patient',
        required=True
    )
    admission = fields.Many2one(
        'medical.appointment',
        string='Admission'
    )
    operating_room = fields.Many2one(
        'medical.center',
        string='Operating Room'
    )
    code = fields.Char(
        'Code',
        readonly=True,
        help="Health Center code / sequence"
    )
    procedures = fields.One2many(
        comodel_name='medical.operation',
        inverse_name='name',
        string='Procedures',
        help="List of the procedures in the surgery. Please enter the first "
        "one as the main procedure"
    )
    supplies = fields.One2many(
        comodel_name='medical.surgery_supply',
        inverse_name='name',
        string='Supplies',
        help="List of the supplies required for the surgery"
    )
    pathology = fields.Many2one(
        comodel_name='medical.pathology',
        strin='Condition',
        help="Base Condition / Reason"
    )
    classification = fields.Selection(
        [
            ('o', 'Optional'),
            ('r', 'Required'),
            ('u', 'Urgent'),
            ('e', 'Emergency'),
        ],
        string='Urgency',
        help="Urgency level for this surgery",
        sort=False
    )
    surgeon = fields.Many2one(
        'medical.practitioner',
        'Surgeon',
        help="Surgeon who did the procedure"
    )
    anesthetist = fields.Many2one(
        'medical.practitioner', 
        'Anesthetist',
        help="Anesthetist in charge"
    )
    surgery_date = fields.Datetime(
        string='Date',
        help="Start of the Surgery"
    )
    surgery_end_date = fields.Datetime(
        'End',
        help="Automatically set when the surgery is done."
            "It is also the estimated end time when confirming the surgery."
    )
    surgery_length = fields.Datetime(
        'Duration',
        states={('invisible', 'state', 'done'),
                ('state', 'signed')},
        help="Length of the surgery",
    )
    state = fields.Selection(
        [
            ('draft', 'Draft'),
            ('confirmed', 'Confirmed'),
            ('cancelled', 'Cancelled'),
            ('in_progress', 'In Progress'),
            ('done', 'Done'),
            ('signed', 'Signed'),
        ], 
        'State',
        readonly=True,
        sort=False
    )
    signed_by = fields.Many2one(
        'medical.practitioner',
        'Signed by',
        readonly=True,
        states={'invisible', 'state', 'signed'},
        help="Health Professional that signed this surgery document"
    )
    # age is deprecated in GNU Health 2.0
    age = fields.Char(
        'Estimative Age',
        help="Use this field for historical purposes, \
        when no date of surgery is given"
    )
    computed_age = fields.Char(
        string='Age',
        help="Computed patient age at the moment of the surgery",
    )
    gender = fields.Selection(
        [
            ('m', 'Male'),
            ('f', 'Female'),
            ('f-m','Female -> Male'),
            ('m-f','Male -> Female'),
        ],
        'Gender',
#        'get_patient_gender',
        searcher='search_patient_gender'
    )
    description = fields.Char(string='Description')
    preop_mallampati = fields.Selection(
        [
            ('Class 1', 'Class 1: Full visibility of tonsils, uvula and soft palate'),
            ('Class 2', 'Class 2: Visibility of hard and soft palate, upper portion of tonsils and uvula'),
            ('Class 3', 'Class 3: Soft and hard palate and base of the uvula are visible'),
            ('Class 4', 'Class 4: Only Hard Palate visible'),
        ],
        'Mallampati Score',
        sort=False
    )
    preop_bleeding_risk = fields.Boolean(
        'Risk of Massive bleeding',
        help="Patient has a risk of losing more than 500 "
        "ml in adults of over 7ml/kg in infants. If so, make sure that "
        "intravenous access and fluids are available"
    )
    preop_oximeter = fields.Boolean(
        'Pulse Oximeter in place',
        help="Pulse oximeter is in place "
        "and functioning"
    )
    preop_site_marking = fields.Boolean(
        'Surgical Site Marking',
        help="The surgeon has marked the surgical incision"
    )
    preop_antibiotics = fields.Boolean(
        'Antibiotic Prophylaxis',
        help="Prophylactic antibiotic treatment within the last 60 minutes"
    )
    preop_sterility = fields.Boolean(
        'Sterility confirmed',
        help="Nursing team has confirmed sterility of the devices and room"
    )
    preop_asa = fields.Selection(
        [
            ('ps1', 'PS 1 : Normal healthy patient'),
            ('ps2', 'PS 2 : Patients with mild systemic disease'),
            ('ps3', 'PS 3 : Patients with severe systemic disease'),
            ('ps4', 'PS 4 : Patients with severe systemic disease that is a constant threat to life '),
            ('ps5', 'PS 5 : Moribund patients who are not expected to survive without the operation'),
            ('ps6', 'PS 6 : A declared brain-dead patient who organs are being removed for donor purposes'),
        ],
        'ASA PS',
        help="ASA pre-operative Physical Status",
        sort=False
    )
    preop_rcri = fields.Many2one(
        'medical.rcri', 'RCRI',
        help='Patient Revised Cardiac Risk Index\n'
        'Points 0: Class I Very Low (0.4% complications)\n'
        'Points 1: Class II Low (0.9% complications)\n'
        'Points 2: Class III Moderate (6.6% complications)\n'
        'Points 3 or more : Class IV High (>11% complications)')
    surgical_wound = fields.Selection(
        [
            ('I', 'Clean . Class I'),
            ('II', 'Clean-Contaminated . Class II'),
            ('III', 'Contaminated . Class III'),
            ('IV', 'Dirty-Infected . Class IV'),
        ],
        'Surgical wound',
        sort=False
    )
    extra_info = fields.Text(string='Extra Info')
    anesthesia_report = fields.Text(string='Anesthesia Report')
    institution = fields.Many2one(
        'res.partner',
        string='Institution'
    )
    report_surgery_date = fields.Date(
        'Surgery Date',
    )
    report_surgery_time = fields.Datetime(
        'Surgery Time',
    )
    surgery_team = fields.One2many(
        comodel_name='medical.surgery_team',
        inverse_name='partner_id',
        string='Team Members',
        help="Professionals Involved in the surgery"
    )
    postoperative_dx = fields.Many2one(
        comodel_name='medical.pathology',
        string='Post-op dx',
        states={('invisible', 'state', 'done'),
                ('state', 'signed')},
        help="Post-operative diagnosis"
    )

    def default_institution():
        HealthInst = self.env['res.partner']
        institution = HealthInst.get_institution()
        return institution

    def default_surgery_date():
        return Datetime.now()

    def default_surgeon():
        HealthProf= self.env['medical.practitioner']
        surgeon = HealthProf.get_practitioner()
        return surgeon

    def default_state():
        return 'draft'

    def get_patient_gender(self, name):
        return self.patient.gender

    def search_patient_gender(self, name, clause):
        res = []
        value = clause[2]
        res.append(('patient.name.gender', clause[1], value))
        return res

    # Show the gender and age upon entering the patient
    # These two are function fields (don't exist at DB level)
    @api.depends('patient')
    def on_change_patient(self):
        gender=None
        age=''
        self.gender = self.patient.gender
        self.computed_age = self.patient.age

    @api.model
    def create(self, vals):
        Sequence = self.env['ir.sequence']
        Config = self.env['medical.sequences']

        vals = [x.copy() for x in vals]
        for values in vals:
            if not values.get('code'):
                config = Config(1)
                values['code'] = Sequence.get_id(
                    config.surgery_code_sequence.id)
        return super(Surgery, self).create(vals)


    def __setup__(self):
        super(Surgery, self).__setup__()
        self._error_messages.update({
            'end_date_before_start': 'End time "%(end_date)s" BEFORE '
                'surgery date "%(surgery_date)s"',
            'or_is_not_available': 'Operating Room is not available'})

        self._order.insert(0, ('surgery_date', 'DESC'))

        self._buttons.update({
            'confirmed': {
                ('invisible', 'state', 'draft'),
                ('state', 'cancelled'),
                },
            'cancel': {
                ('invisible', 'state', 'confirmed'),
                },
            'start': {
                ('invisible', 'state', 'confirmed'),
                },
            'done': {
                ('invisible', 'state', 'in_progress'),
                },
            'signsurgery': {
                ('invisible', 'state', 'done'),
                },
            }
        )

    def validate(self, surgeries):
        super(Surgery, self).validate(surgeries)
        for surgery in surgeries:
            surgery.validate_surgery_period()

    def validate_surgery_period(self):
        Lang = self.env['ir.lang']

        language, = Lang.search([
            ('code', '=', Transaction().language),
            ])
        if (self.surgery_end_date and self.surgery_date):
            if (self.surgery_end_date < self.surgery_date):
                self.raise_user_error('end_date_before_start', {
                        'surgery_date': Lang.strftime(self.surgery_date,
                            language.code,
                            language.date),
                        'end_date': Lang.strftime(self.surgery_end_date,
                            language.code,
                            language.date),
                        })

    def write(self, surgeries, vals):
        # Don't allow to write the record if the surgery has been signed
        if surgeries[0].state == 'signed':
            self.raise_user_error(
                "This surgery is at state Done and has been signed\n"
                "You can no longer modify it.")
        return super(Surgery, self).write(surgeries, vals)

    ## Method to check for availability and make the Operating Room reservation
     # for the associated surgery

    def confirmed(self, surgeries):
        surgery_id = surgeries[0]
        Operating_room = self.env['medical.center']
        cursor = Transaction().connection.cursor()

        # Operating Room and end surgery time check
        if (not surgery_id.operating_room or not surgery_id.surgery_end_date):
            self.raise_user_error("Operating Room and estimated end time  "
            "are needed in order to confirm the surgery")

        or_id = surgery_id.operating_room.id
        cursor.execute("SELECT COUNT(*) \
            FROM gnuhealth_surgery \
            WHERE (surgery_date::timestamp,surgery_end_date::timestamp) \
                OVERLAPS (timestamp %s, timestamp %s) \
              AND (state = %s or state = %s) \
              AND operating_room = CAST(%s AS INTEGER) ",
            (surgery_id.surgery_date,
            surgery_id.surgery_end_date,
            'confirmed', 'in_progress', str(or_id)))
        res = cursor.fetchone()
        if (surgery_id.surgery_end_date <
            surgery_id.surgery_date):
            self.raise_user_error("The Surgery end date must later than the \
                Start")
        if res[0] > 0:
            self.raise_user_error('or_is_not_available')
        else:
            self.write(surgeries, {'state': 'confirmed'})

    # Cancel the surgery and set it to draft state
    # Free the related Operating Room

    def cancel(self, surgeries):
        surgery_id = surgeries[0]
        Operating_room = self.env['medical.center']

        self.write(surgeries, {'state': 'cancelled'})

    # Start the surgery
    def start(self, surgeries):
        surgery_id = surgeries[0]
        Operating_room = self.env['medical.center']

        self.write(surgeries,
            {'state': 'in_progress',
             'surgery_date': Datetime.now(),
             'surgery_end_date': Datetime.now()})
        Operating_room.write([surgery_id.operating_room], {'state': 'occupied'})

    # Finnish the surgery
    # Free the related Operating Room

    def done(self, surgeries):
        surgery_id = surgeries[0]
        Operating_room = self.env['medical.center']

        self.write(surgeries, {'state': 'done',
                              'surgery_end_date': Datetime.now()})

        Operating_room.write([surgery_id.operating_room], {'state': 'free'})

    # Sign the surgery document, and the surgical act.

    def signsurgery(self, surgeries):
        surgery_id = surgeries[0]

        # Sign, change the state of the Surgery to "Signed"
        # and write the name of the signing health professional

        signing_hp = self.env['medical.practitioner'].get_practitioner()
        if not signing_hp:
            self.raise_user_error(
                "No health professional associated to this user !")

        self.write(surgeries, {
            'state': 'signed',
            'signed_by': signing_hp})

    def get_report_surgery_date(self, name):
        Company = self.env('company.company')

        timezone = None
        company_id = Transaction().context.get('company')
        if company_id:
            company = Company(company_id)
            if company.timezone:
                timezone = pytz.timezone(company.timezone)

        dt = self.surgery_date
        return Datetime.astimezone(dt.replace(tzinfo=pytz.utc), timezone).date()

    def get_report_surgery_time(self, name):
        Company = self.env['company.company']

        timezone = None
        company_id = Transaction().context.get('company')
        if company_id:
            company = Company(company_id)
            if company.timezone:
                timezone = pytz.timezone(company.timezone)

        dt = self.surgery_date
        return Datetime.astimezone(dt.replace(tzinfo=pytz.utc), timezone).time()

    def search_rec_name(self, name, clause):
        if clause[1].startswith('!') or clause[1].startswith('not '):
            bool_op = 'AND'
        else:
            bool_op = 'OR'
        return [
            bool_op,
            ('patient',) + tuple(clause[1:]),
            ('code',) + tuple(clause[1:]),
            ]
Exemplo n.º 16
0
class HrIqama(models.Model):
    _name = 'hr.iqama'
    _order = 'id desc'
    _inherit = 'mail.thread'
    _description = 'HR IQAMA'

    @api.one
    @api.depends('birthdate')
    def _get_age(self):
        """
            Calculate age, based on inputed birthdate
        """
        if self.birthdate:
            try:
                birthdate = datetime.strptime(self.birthdate, '%Y-%m-%d')
                age_year = (datetime.now() - birthdate).days / 365
                self.age = age_year
            except:
                self.age = 0.0
        else:
            self.age = 0.0

    name = fields.Char('Name(As in Passport)',
                       size=50,
                       help="Name of the dependent",
                       required=True)
    arabic_name = fields.Char('Arabic Name', size=50)
    relation = fields.Selection([('employee', 'Self'), ('child', 'Child'),
                                 ('spouse', 'Spouse')], 'Relation')
    employee_id = fields.Many2one(
        'hr.employee',
        'Employee',
        default=lambda self: self.env['hr.employee'].get_employee())
    job_id = fields.Many2one('hr.job', readonly=True, string='Job Position')
    department_id = fields.Many2one('hr.department',
                                    readonly=True,
                                    string='Department')
    branch_id = fields.Many2one('hr.branch', readonly=True, string='Office')
    company_id = fields.Many2one('res.company',
                                 readonly=True,
                                 string='Company',
                                 default=lambda self: self.env.user.company_id)
    iqama_no = fields.Char('IQAMA Number', size=32, copy=False)
    issue_date = fields.Date('Date of Issue', copy=False)
    expiry_date = fields.Date('Date of Expiry', copy=False)
    profession = fields.Char('Profession', size=64, readonly=False)
    serial_number = fields.Char('Serial Number', copy=False)
    place_of_issue = fields.Char('Place of Issue', copy=False)
    nationality = fields.Many2one('res.country', 'Nationality', readonly=False)
    religion = fields.Selection([('muslim', 'Muslim'),
                                 ('non-muslim', 'Non Muslim')],
                                'Religion',
                                default="muslim")
    state = fields.Selection([('draft', 'Draft'),
                              ('confirm', 'Waiting Approval'),
                              ('validate', 'Approved'),
                              ('inprogress', 'In Progress'),
                              ('received', 'Issued'),
                              ('need_renewal', 'To be Renewed'),
                              ('refuse', 'Refused')],
                             'Status',
                             default='draft')
    request_type = fields.Selection([('employee', 'Employee'),
                                     ('family', 'Family'),
                                     ('new_born', 'New Born Baby')],
                                    'Type',
                                    required=True,
                                    default='employee')
    approved_date = fields.Datetime('Approved Date', readonly=True, copy=False)
    approved_by = fields.Many2one('res.users',
                                  'Approved by',
                                  readonly=True,
                                  copy=False)
    refused_by = fields.Many2one('res.users',
                                 'Refused by',
                                 readonly=True,
                                 copy=False)
    refused_date = fields.Datetime('Refused on', readonly=True, copy=False)
    age = fields.Float(compute='_get_age', string="Age")
    birthdate = fields.Date("Date of Birth")
    is_saudi = fields.Boolean("Is Saudi")
    description = fields.Text('Description')
    handled_by_id = fields.Many2one('hr.employee', 'Handled By')
    hijri_expiry_date = fields.Char('Date of Expiry(Hijri)')
    iqama_position = fields.Char('IQAMA Position', copy=False)
    arrival_date = fields.Date('Arrival Date(In KSA)')
    current_status = fields.Boolean('In Saudi?')

    @api.onchange('expiry_date', 'issue_date')
    def onchange_expiry_date(self):
        """
            check expiry date is lower than issue date
            return: warning
        """
        if self.expiry_date and self.issue_date and self.expiry_date < self.issue_date:
            raise ValidationError(
                _('Issue date must be greater then Expiry date.'))

    @api.onchange('request_type')
    def onchange_request_type(self):
        """
            check the request_type, Employee, Family or New Born,
        """
        if self.request_type != 'employee' or not self.employee_id:
            self.birthdate = False
            self.arabic_name = False
            self.name = False
        else:
            self.birthdate = self.employee_id.sudo().birthday or False
            self.arabic_name = self.employee_id.arabic_name or False
            self.name = self.employee_id.name

    @api.onchange('employee_id')
    def onchange_employee_id(self):
        """
            auto change the values like, Profession, Job, Department, etc depends on selected Employee.
        """
        self.arabic_name = self.employee_id.arabic_name
        self.profession = self.employee_id.sudo().job_id.name
        self.job_id = self.employee_id.sudo().job_id.id
        self.branch_id = self.employee_id.branch_id.id
        self.company_id = self.employee_id.company_id.id
        self.department_id = self.employee_id.department_id.id
        self.nationality = self.employee_id.sudo().country_id.id
        self.religion = self.employee_id.religion
        self.is_saudi = self.employee_id.is_saudi
        if self.request_type == 'employee':
            self.birthdate = self.employee_id.sudo().birthday
            self.name = self.employee_id.name

    @api.model
    def create(self, values):
        """
            Create a new record
            :return: Newly created record ID
        """
        partner = []
        if values.get('employee_id', False):
            employee_id = self.env['hr.employee'].browse(values['employee_id'])
            values.update({
                'job_id': employee_id.job_id.id,
                'department_id': employee_id.department_id.id,
                'company_id': employee_id.company_id.id,
                'branch_id': employee_id.branch_id.id,
            })
        res = super(HrIqama, self).create(values)
        if res.employee_id.parent_id.user_id:
            partner.append(res.employee_id.parent_id.user_id.partner_id.id)
        if res.employee_id.user_id:
            partner.append(res.employee_id.user_id.partner_id.id)
        channel_id = self.env.ref('saudi_hr.manager_channel').id
        res.message_subscribe(partner_ids=partner, channel_ids=[channel_id])
        return res

    @api.multi
    def write(self, values):
        """
            Update an existing record.
            :param values: Current record fields data
            :return: Current update record ID
        """
        partner = []
        if values.get('employee_id', False):
            employee_id = self.env['hr.employee'].browse(values['employee_id'])
            values.update({
                'job_id': employee_id.job_id.id,
                'department_id': employee_id.department_id.id,
                'company_id': employee_id.company_id.id,
                'branch_id': employee_id.branch_id.id or False,
            })
            if employee_id.user_id:
                partner.append(employee_id.user_id.partner_id.id)
            if employee_id.parent_id.user_id:
                partner.append(employee_id.parent_id.user_id.partner_id.id)
        # channel_id=self.env.ref('saudi_hr.manager_channel').id
        self.message_subscribe(partner_ids=partner)
        return super(HrIqama, self).write(values)

    @api.multi
    def unlink(self):
        """
            Delete/ remove selected record
            :return: Deleted record ID
        """
        for line in self:
            if line.state not in ['draft']:
                raise UserError(
                    _('You cannot remove the IQAMA record which is not in draft state!'
                      ))
            return super(HrIqama, line).unlink()

    @api.model
    def check_iqama_expiry(self):
        """
            Send mail for IQAMA Expiry
        """
        template_id = self.env.ref('saudi_hr_iqama.hr_iqama_expiration_email')
        today_date = str(fields.Date.today())
        next_date = datetime.strptime(
            str(fields.Date.today()),
            DEFAULT_SERVER_DATE_FORMAT) + timedelta(days=30)
        next_date = datetime.strftime(next_date, DEFAULT_SERVER_DATE_FORMAT)
        for iqama in self.search([('state', '=', 'received'),
                                  ('expiry_date', '>=', today_date),
                                  ('expiry_date', '<=', next_date)]):
            diff = datetime.strptime(str(iqama.expiry_date), DEFAULT_SERVER_DATE_FORMAT) - \
                   datetime.strptime(str(fields.Date.today()), DEFAULT_SERVER_DATE_FORMAT)
            if diff.days == 10 and template_id:
                template_id.send_mail(iqama.id,
                                      force_send=False,
                                      raise_exception=False)
            if diff.days <= 10:
                iqama.iqama_need_renewal()

    @api.multi
    def iqama_confirm(self):
        """
            sent the status of generating his/her iqama in confirm state
        """
        gr_groups_config_obj = self.env['hr.groups.configuration']
        for iqama in self:
            iqama.state = 'confirm'
            gr_groups_config_ids = gr_groups_config_obj.search([
                ('branch_id', '=', iqama.employee_id.branch_id.id or False),
                ('gr_ids', '!=', False)
            ])
            user_ids = gr_groups_config_ids and [
                employee.user_id.id
                for employee in gr_groups_config_ids.gr_ids if employee.user_id
            ] or []
            iqama.sudo().message_subscribe_users(user_ids=user_ids)

    @api.multi
    def iqama_inprogress(self):
        """
            sent the status of generating his/her iqama in inprogress state
        """
        for iqama in self:
            iqama.state = 'inprogress'
            iqama.message_post(message_type="email",
                               subtype='mail.mt_comment',
                               body=_('IQAMA request is In progress'))

    @api.multi
    def iqama_refuse(self):
        """
            sent the status of generating his/her iqama in refuse state
        """
        for iqama in self:
            iqama.write({
                'state': 'refuse',
                'refused_by': self.env.uid,
                'refused_date': datetime.today()
            })
            iqama.message_post(message_type="email",
                               subtype='mail.mt_comment',
                               body=_('IQAMA request is Refused'))

    @api.multi
    def iqama_received(self):
        """
            sent the status of generating his/her iqama in received state by user
        """
        for iqama in self:
            iqama.state = 'received'
            iqama.message_post(message_type="email",
                               subtype='mail.mt_comment',
                               body=_('IQAMA request is Received'))

    @api.multi
    def iqama_validate(self):
        """
            sent the status of generating his/her iqama in validate state
        """
        for iqama in self:
            iqama.write({
                'state': 'validate',
                'approved_by': self.env.uid,
                'approved_date': datetime.today()
            })
            iqama.message_post(message_type="email",
                               subtype='mail.mt_comment',
                               body=_('IQAMA request is Validated'))

    @api.multi
    def iqama_set_to_draft(self):
        """
            sent the status of generating his/her iqama in draft state
        """
        for iqama in self:
            iqama.write({
                'state': 'draft',
                'approved_by': False,
                'approved_date': False,
                'refused_by': False,
                'refused_date': False
            })
            iqama.message_post(message_type="email",
                               subtype='mail.mt_comment',
                               body=_('IQAMA request is reset to draft'))

    @api.multi
    def iqama_need_renewal(self):
        """
            sent the status of generating his/her iqama in renewal state
        """
        for iqama in self:
            iqama.state = 'need_renewal'
            iqama.message_post(message_type="email",
                               subtype='mail.mt_comment',
                               body=_('IQAMA need renewal'))
Exemplo n.º 17
0
class AreluxSaleReportLine(models.Model):
    _name = 'arelux.sale.report.line'
    _description = 'Arelux Sale Report Line'
    _order = "position asc"    
        
    arelux_sale_report_id = fields.Many2one(
        comodel_name='arelux.sale.report',
        string='Arelux Sale Report'
    )
    arelux_sale_report_type_id = fields.Many2one(
        comodel_name='arelux.sale.report.type',
        string='Arelux Sale Report Type'
    )
    position = fields.Integer(
        string='Posicion'
    )                                       
    ar_qt_activity_type = fields.Selection(
        selection=[
            ('none','Ninguno'), 
            ('arelux','Arelux'), 
            ('todocesped','Todocesped'),
            ('evert','Evert')                         
        ],
        string='Tipo de actividad',
        default='none'
    )
    ar_qt_customer_type = fields.Selection(
        selection=[
            ('none','Ninguno'), 
            ('particular','Particular'), 
            ('profesional','Profesional')                         
        ],
        string='Tipo de cliente',
        default='none'
    )
    crm_team_id = fields.Many2one(
        comodel_name='crm.team',
        string='Equipo de ventas'
    )
    response_type = fields.Text(
        string='Response type'
    )
    response_result_value = fields.Text(
        string='Response result value'
    )
    group_by_user = fields.Boolean(
        default=False,
        string='Group by user'
    )
    show_in_table_format = fields.Boolean(
        default=False,
        string='Show in table format'
    )    
    user_line = fields.One2many('arelux.sale.report.line.user', 'arelux_sale_report_line_id', string='User Lines', copy=True)
    sale_order_line = fields.One2many('arelux.sale.report.line.sale.order', 'arelux_sale_report_line_id', string='Sale Order Lines', copy=True)    
    
    @api.one
    def remove_all_user_line(self):
        arelux_sale_report_line_user_ids = self.env['arelux.sale.report.line.user'].search([('arelux_sale_report_line_id', '=', self.id)])            
        if len(arelux_sale_report_line_user_ids)>0:
            for arelux_sale_report_line_user_id in arelux_sale_report_line_user_ids:
                arelux_sale_report_line_user_id.unlink()
                
    @api.one
    def remove_all_sale_order_line(self):
        arelux_sale_report_line_sale_order_ids = self.env['arelux.sale.report.line.sale.order'].search([('arelux_sale_report_line_id', '=', self.id)])            
        if len(arelux_sale_report_line_sale_order_ids)>0:
            for arelux_sale_report_line_sale_order_id in arelux_sale_report_line_sale_order_ids:
                arelux_sale_report_line_sale_order_id.unlink()                        
    
    @api.one
    def _get_line_info_real(self, custom_type):
        return_values = {
            'response_type': '',
            'response_result_value': ''
        }
        
        if custom_type=='sale_order_done_amount_untaxed':            
            search_filters = [
                ('state', 'in', ('sale', 'done')),
                ('amount_untaxed', '>', 0),
                ('claim', '=', False),
                ('confirmation_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('confirmation_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
            #sale_team_id
            if self.crm_team_id.id>0:
                search_filters.append(('sale_team_id', '=', self.crm_team_id.id))
            
            sale_order_ids = self.env['sale.order'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'sum'
                amount_untaxed = sum(sale_order_ids.mapped('amount_untaxed'))                                
                return_values['response_result_value'] = amount_untaxed
                return_values['amount_untaxed'] = amount_untaxed
            else:
                return_values['response_type'] = 'list_by_user_id'                        
            
                res_users = {}
                if len(sale_order_ids)>0:
                    for sale_order_id in sale_order_ids:
                        #fix if need create
                        user_id = int(sale_order_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'amount_untaxed': 0
                            }
                        
                            if user_id>0:
                                res_users[user_id]['name'] = sale_order_id.user_id.name                                
                        #sum
                        res_users[user_id]['amount_untaxed'] += sale_order_id.amount_untaxed
                #fix response_result_value
                return_values['response_result_value'] = 'amount_untaxed'                                 
                return_values['amount_untaxed'] = sum(sale_order_ids.mapped('amount_untaxed'))                    
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(sale_order_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'amount_untaxed': res_user['amount_untaxed']                                                                       
                        }
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
        
        elif custom_type=='sale_order_done_count':                                            
            search_filters = [
                ('state', 'in', ('sale', 'done')),
                ('amount_untaxed', '>', 0),
                ('claim', '=', False),
                ('confirmation_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('confirmation_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
            #sale_team_id
            if self.crm_team_id.id>0:
                search_filters.append(('sale_team_id', '=', self.crm_team_id.id))
            
            sale_order_ids = self.env['sale.order'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'count'                                
                return_values['response_result_value'] = len(sale_order_ids)
                return_values['count'] = len(sale_order_ids)
            else:
                return_values['response_type'] = 'list_by_user_id'                        
            
                res_users = {}
                if len(sale_order_ids)>0:
                    for sale_order_id in sale_order_ids:
                        #fix if need create
                        user_id = int(sale_order_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'count': 0
                            }
                            
                            if user_id>0:
                                res_users[user_id]['name'] = sale_order_id.user_id.name
                        #sum
                        res_users[user_id]['count'] += 1
                #fix response_result_value                    
                return_values['response_result_value'] = 'count'
                return_values['count'] = len(sale_order_ids)
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(sale_order_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'count': res_user['count']                                                                       
                        }
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)                                    
    
        elif custom_type=='sale_order_ticket_medio':            
            search_filters = [
                ('state', 'in', ('sale', 'done')),
                ('amount_untaxed', '>', 0),
                ('claim', '=', False),
                ('confirmation_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('confirmation_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
            #sale_team_id
            if self.crm_team_id.id>0:
                search_filters.append(('sale_team_id', '=', self.crm_team_id.id))
            
            sale_order_ids = self.env['sale.order'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'sum'
            
                amount_untaxed = sum(sale_order_ids.mapped('amount_untaxed'))
                
                ticket_medio = 0
                if len(sale_order_ids)>0:
                    ticket_medio = (amount_untaxed/len(sale_order_ids))
                
                ticket_medio = "{0:.2f}".format(ticket_medio)
                                        
                return_values['response_result_value'] = ticket_medio
                return_values['ticket_medio'] = ticket_medio
            else:
                return_values['response_type'] = 'list_by_user_id'                        
            
                res_users = {}
                if len(sale_order_ids)>0:
                    for sale_order_id in sale_order_ids:
                        #fix if need create
                        user_id = int(sale_order_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'count': 0,
                                'amount_untaxed': 0
                            }
                            
                            if user_id>0:
                                res_users[user_id]['name'] = sale_order_id.user_id.name
                        #sum
                        res_users[user_id]['count'] += 1
                        res_users[user_id]['amount_untaxed'] += sale_order_id.amount_untaxed
                #fix response_result_value                                   
                return_values['response_result_value'] = 'amount_untaxed'
                
                amount_untaxed = sum(sale_order_ids.mapped('amount_untaxed'))
                
                ticket_medio = 0
                if len(sale_order_ids)>0:
                    ticket_medio = (amount_untaxed/len(sale_order_ids))
                
                ticket_medio = "{0:.2f}".format(ticket_medio)                                        
                return_values['ticket_medio'] = ticket_medio
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(sale_order_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'amount_untaxed': 0                                                                       
                        }
                        
                        if res_user['count']>0:
                            arelux_sale_report_line_user_vals['amount_untaxed'] = (res_user['amount_untaxed']/res_user['count'])
                        
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
                            
        elif custom_type=='sale_order_sent_count':            
            search_filters = [
                ('date_order_management', '!=', False),
                ('amount_untaxed', '>', 0),
                ('opportunity_id', '!=', False),
                ('claim', '=', False),
                ('date_order_management', '>=', self.arelux_sale_report_id.date_from_filter),
                ('date_order_management', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
            #sale_team_id
            if self.crm_team_id.id>0:
                search_filters.append(('sale_team_id', '=', self.crm_team_id.id))
            
            sale_order_ids = self.env['sale.order'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'count'            
                return_values['response_result_value'] = len(sale_order_ids)
                return_values['count'] = len(sale_order_ids)
            else:
                return_values['response_type'] = 'list_by_user_id'                        
            
                res_users = {}
                if len(sale_order_ids)>0:
                    for sale_order_id in sale_order_ids:
                        #fix if need create
                        user_id = int(sale_order_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'count': 0
                            }
                            
                            if user_id>0:
                                res_users[user_id]['name'] = sale_order_id.user_id.name
                        #sum
                        res_users[user_id]['count'] += 1
                #fix response_result_value                    
                return_values['response_result_value'] = 'count'
                return_values['count'] = len(sale_order_ids)
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(sale_order_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'count': res_user['count']                                                                       
                        }
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
        
        elif custom_type=='sale_order_done_muestras':            
            search_filters = [
                ('state', 'in', ('sale', 'done')),
                ('amount_untaxed', '=', 0),
                ('claim', '=', False),
                ('carrier_id', '!=', False),
                ('carrier_id.carrier_type', '=', 'nacex'),
                ('confirmation_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('confirmation_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
            #sale_team_id
            if self.crm_team_id.id>0:
                search_filters.append(('sale_team_id', '=', self.crm_team_id.id))
            
            sale_order_ids = self.env['sale.order'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'count'            
                return_values['response_result_value'] = len(sale_order_ids)
                return_values['count'] = len(sale_order_ids)
            else:
                return_values['response_type'] = 'list_by_user_id'                        
            
                res_users = {}
                if len(sale_order_ids)>0:
                    for sale_order_id in sale_order_ids:
                        #fix if need create
                        user_id = int(sale_order_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'count': 0
                            }
                            
                            if user_id>0:
                                res_users[user_id]['name'] = sale_order_id.user_id.name
                        #sum
                        res_users[user_id]['count'] += 1
                #fix response_result_value                    
                return_values['response_result_value'] = 'count'
                return_values['count'] = len(sale_order_ids)
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(sale_order_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'count': res_user['count']                                                                       
                        }
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
        
        elif custom_type=='res_partner_potencial_count':
            return_values['response_type'] = 'list_by_user_id'            
            
            search_filters = [
                ('type', '=', 'contact'),
                ('active', '=', True),
                ('create_uid', '!=', 1),
                ('create_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('create_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))            
            
            res_users = {}
            res_partner_ids = self.env['res.partner'].search(search_filters)
            if len(res_partner_ids)>0:
                for res_partner_id in res_partner_ids:
                    #fix if need create
                    user_id = int(res_partner_id.create_uid.id)
                    if user_id not in res_users:
                        res_users[user_id] = {
                            'id': user_id,
                            'name': 'Sin comercial',
                            'total': 0
                        }
                        
                        if user_id>0:
                            res_users[user_id]['name'] = res_partner_id.create_uid.name
                    #sum
                    res_users[user_id]['total'] += 1
            #fix response_result_value
            return_values['response_result_value'] = 'count'                
            #remove_all
            self.remove_all_user_line()                                               
            #creates
            if len(res_partner_ids)>0:
                for res_user_id, res_user in res_users.items():
                    arelux_sale_report_line_user_vals = {
                        'arelux_sale_report_line_id': self.id,
                        'user_id': res_user['id'],
                        'count': res_user['total']                                                                       
                    }
                    arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
                    
        elif custom_type=='cartera_actual_activa_count':                                    
            search_filters = [
                ('type', '=', 'contact'),
                ('active', '=', True),
                ('user_id', '!=', False),
                ('sale_order_amount_untaxed_year_now', '>', 0),
                ('create_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('create_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))            
            
            res_partner_ids = self.env['res.partner'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'count'                        
                return_values['response_result_value'] = len(res_partner_ids)
                return_values['count'] = len(res_partner_ids)
            else:
                return_values['response_type'] = 'list_by_user_id'                        
            
                res_users = {}
                if len(res_partner_ids)>0:
                    for res_partner_id in res_partner_ids:
                        #fix if need create
                        user_id = int(res_partner_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'count': 0
                            }
                            
                            if user_id>0:
                                res_users[user_id]['name'] = res_partner_id.user_id.name
                        #sum
                        res_users[user_id]['count'] += 1
                #fix response_result_value                    
                return_values['response_result_value'] = 'count'
                return_values['count'] = len(res_partner_ids)
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(res_partner_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'count': res_user['count']                                                                       
                        }
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
        
        elif custom_type=='cartera_actual_count':                        
            search_filters = [
                ('type', '=', 'contact'),
                ('active', '=', True),
                ('user_id', '!=', False),
                ('create_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('create_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))            
            
            res_partner_ids = self.env['res.partner'].search(search_filters)
            
            if self.group_by_user==False:
                return_values['response_type'] = 'count'
                return_values['response_result_value'] = len(res_partner_ids)
                return_values['count'] = len(res_partner_ids)
            else:
                return_values['response_type'] = 'list_by_user_id'
                res_users = {}            
                if len(res_partner_ids)>0:
                    for res_partner_id in res_partner_ids:
                        #fix if need create
                        user_id = int(res_partner_id.user_id.id)
                        if user_id not in res_users:
                            res_users[user_id] = {
                                'id': user_id,
                                'name': 'Sin comercial',
                                'total': 0
                            }
                            
                            if user_id>0:
                                res_users[user_id]['name'] = res_partner_id.user_id.name
                        #sum
                        res_users[user_id]['total'] += 1
                #fix response_result_value
                return_values['response_result_value'] = 'count'
                return_values['count'] = len(res_partner_ids)                    
                #remove_all
                self.remove_all_user_line()                                               
                #creates
                if len(res_partner_ids)>0:
                    for res_user_id, res_user in res_users.items():
                        arelux_sale_report_line_user_vals = {
                            'arelux_sale_report_line_id': self.id,
                            'user_id': res_user['id'],
                            'count': res_user['total']                                                                       
                        }
                        arelux_sale_report_line_user_obj = self.env['arelux.sale.report.line.user'].sudo().create(arelux_sale_report_line_user_vals)
        
        elif custom_type=='nuevos_clientes_con_ventas':
            return_values['response_type'] = 'list_sale_orders'
            #partner_ids with sale_orders < date_from
            res_partner_ids = []
            search_filters = [
                ('state', 'in', ('sale', 'done')),
                ('claim', '=', False),
                ('amount_untaxed', '>', 0),
                ('confirmation_date', '<', self.arelux_sale_report_id.date_from_filter),
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
                
            sale_order_ids = self.env['sale.order'].search(search_filters)            
            if len(sale_order_ids)>0:
                for sale_order_id in sale_order_ids:
                    if sale_order_id.partner_id.id not in res_partner_ids:
                        res_partner_ids.append(sale_order_id.partner_id.id)                
            #sale_orders with filters and partner_id not in                                        
            search_filters = [
                ('state', 'in', ('sale', 'done')),
                ('claim', '=', False),
                ('amount_untaxed', '>', 0),
                ('partner_id', 'not in', res_partner_ids),
                ('confirmation_date', '>=', self.arelux_sale_report_id.date_from_filter),
                ('confirmation_date', '<=', self.arelux_sale_report_id.date_to_filter)
            ]
            #ar_qt_activity_type
            if self.ar_qt_activity_type!='none':
                search_filters.append(('ar_qt_activity_type', '=', self.ar_qt_activity_type))
            #ar_qt_customer_type
            if self.ar_qt_customer_type!='none':
                search_filters.append(('ar_qt_customer_type', '=', self.ar_qt_customer_type))
                
            sale_order_ids = self.env['sale.order'].search(search_filters)
            #sort_custom
            sale_order_ids2 = []
            if len(sale_order_ids)>0:
                for sale_order_id in sale_order_ids:
                    sale_order_ids2.append({
                        'id': sale_order_id.id,
                        'user_name': sale_order_id.user_id.name
                    })
                sale_order_ids = sorted(sale_order_ids2, key=operator.itemgetter('user_name'))
            #remove_all
            self.remove_all_sale_order_line()
            #creates
            if len(sale_order_ids)>0:
                for sale_order_id in sale_order_ids:
                    arelux_sale_report_line_sale_order_vals = {
                        'arelux_sale_report_line_id': self.id,
                        'sale_order_id': sale_order_id['id']                                                                
                    }
                    arelux_sale_report_line_sale_order_obj = self.env['arelux.sale.report.line.sale.order'].sudo().create(arelux_sale_report_line_sale_order_vals)                                                                                                                                                                                                                                  
        
        return return_values
    
    @api.one
    def _get_line_info(self):        
        if self.arelux_sale_report_type_id.id>0:
            
            if self.arelux_sale_report_type_id.custom_type=='ratio_muestras':            
                if self.group_by_user==False:
                    return_values_sale_order_done_muestras = self._get_line_info_real('sale_order_done_muestras')[0]
                    return_values_sale_order_sent_count = self._get_line_info_real('sale_order_sent_count')[0]
                    
                    self.response_type = 'percent'
                    ratio_muestras = 0                
                    
                    if return_values_sale_order_done_muestras['count']>0 and return_values_sale_order_sent_count['count']>0:
                        ratio_muestras = (float(return_values_sale_order_done_muestras['count'])/float(return_values_sale_order_sent_count['count']))*100  
                    
                    ratio_muestras = "{0:.2f}".format(ratio_muestras)
                    
                    self.response_result_value = ratio_muestras
                else:
                    self.response_type = 'percent'
                    self.response_result_value = 'percent'
            
            elif self.arelux_sale_report_type_id.custom_type=='ratio_calidad':
                if self.group_by_user==False:
                    self.response_type = 'percent'
                    
                    return_values_sale_order_done_count = self._get_line_info_real('sale_order_done_count')[0]
                    return_values_sale_order_sent_count = self._get_line_info_real('sale_order_sent_count')[0]
                    
                    ratio_calidad = 0
                    
                    if return_values_sale_order_done_count['count']>0 and return_values_sale_order_sent_count['count']>0:
                        ratio_calidad = (float(return_values_sale_order_done_count['count'])/float(return_values_sale_order_sent_count['count']))*100
                    
                    ratio_calidad = "{0:.2f}".format(ratio_calidad)
                    
                    self.response_result_value = ratio_calidad
                else:
                    self.response_type = 'percent'
                    self.response_result_value = 'percent'
                    
            elif self.arelux_sale_report_type_id.custom_type=='line_break':
                self.response_type = 'line_break'                                           
            else:
                return_values = self._get_line_info_real(self.arelux_sale_report_type_id.custom_type)[0]
            
                self.response_type = return_values['response_type']
                self.response_result_value = return_values['response_result_value']
                                                                                                       
        #_logger.info(response)                        
        #return response                                                    
    class MuskathlonRegistrationForm(models.AbstractModel):
        _name = 'cms.form.event.registration.muskathlon'
        _inherit = ['cms.form.payment', 'cms.form.event.match.partner']

        # The form is inside a Muskathlon details page
        form_buttons_template = 'cms_form_compassion.modal_form_buttons'
        form_id = 'modal_muskathlon_registration'
        _form_model = 'event.registration'
        _form_required_fields = [
            'ambassador_picture_1', 'ambassador_quote', 'sport_level',
            'sport_level_description', 'gtc_accept', 't_shirt_size',
            't_shirt_type', 'passport_number', 'passport_expiration_date',
            'emergency_name', 'emergency_phone', 'birth_name'
        ]
        _payment_accept_redirect = '/muskathlon_registration/payment/validate'

        invoice_id = fields.Many2one('account.invoice')
        ambassador_picture_1 = fields.Binary('Profile picture')
        ambassador_quote = fields.Text(
            'My motto',
            default="",
            help="Write a small quote that will appear on your profile page "
            "and will be used in thank you letters your donors will "
            "receive.")

        t_shirt_size = fields.Selection([('XS', 'XS'), ('S', 'S'), ('M', 'M'),
                                         ('L', 'L'), ('XL', 'XL'),
                                         ('XXL', 'XXL')])
        t_shirt_type = fields.Selection([
            ('singlet', 'Singlet'),
            ('shirt', 'Shirt'),
            ('bikeshirt', 'Bikeshirt'),
        ])
        gtc_accept = fields.Boolean("Terms and conditions", required=True)
        birth_name = fields.Char('Passport name',
                                 help='Your name as printed on your passport')
        emergency_name = fields.Char(
            help='Please indicate a contact in case of emergency '
            'during the trip.')

        @property
        def discipline_ids(self):
            return self.event_id.sport_discipline_ids.ids

        @property
        def _form_fieldsets(self):
            fieldset = [{
                'id':
                'sport',
                'title':
                _('Your sport profile'),
                'description':
                '',
                'fields': [
                    'ambassador_picture_1', 'sport_discipline_id',
                    'sport_level', 'sport_level_description',
                    'ambassador_quote', 't_shirt_size', 't_shirt_type',
                    'event_id'
                ]
            }, {
                'id':
                'partner',
                'title':
                _('Your coordinates'),
                'description':
                '',
                'fields': [
                    'partner_title', 'partner_firstname', 'partner_lastname',
                    'partner_email', 'partner_phone', 'partner_street',
                    'partner_zip', 'partner_city', 'partner_country_id'
                ]
            }, {
                'id':
                'trip',
                'title':
                _('Information for the trip'),
                'fields': [
                    'birth_name', 'passport_number',
                    'passport_expiration_date', 'emergency_name',
                    'emergency_phone'
                ]
            }]
            if self.event_id.total_price:
                fieldset.append({
                    'id':
                    'payment',
                    'title':
                    _('Registration payment'),
                    'description':
                    _('For validating registrations, we ask a fee of '
                      'CHF %s that you can directly pay with your '
                      'Postfinance or Credit Card') %
                    str(self.event_id.total_price),
                    'fields':
                    ['amount', 'currency_id', 'acquirer_ids', 'gtc_accept']
                })
            else:
                fieldset.append({'id': 'gtc', 'fields': ['gtc_accept']})
            return fieldset

        @property
        def form_widgets(self):
            # Hide fields
            res = super(MuskathlonRegistrationForm, self).form_widgets
            res.update({
                'event_id': 'cms_form_compassion.form.widget.hidden',
                'amount': 'cms_form_compassion.form.widget.hidden',
                'ambassador_picture_1':
                'cms_form_compassion.form.widget.simple.image',
                'gtc_accept': 'cms_form_compassion.form.widget.terms',
            })
            return res

        @property
        def _default_amount(self):
            return self.event_id.total_price

        @property
        def form_title(self):
            if self.event_id:
                return _("Registration for ") + self.event_id.name
            else:
                return _("New registration")

        @property
        def submit_text(self):
            if self.event_id.total_price:
                return _("Proceed with payment")
            else:
                return _("Register now")

        @property
        def gtc(self):
            html_file = file_open(
                'muskathlon/static/src/html/muskathlon_gtc_{}.html'.format(
                    self.env.lang))
            text = html_file.read()
            html_file.close()
            return text

        def form_init(self, request, main_object=None, **kw):
            form = super(MuskathlonRegistrationForm,
                         self).form_init(request, main_object, **kw)
            # Set default values
            form.event_id = kw.get('event').sudo().odoo_event_id
            return form

        def form_get_request_values(self):
            """ Save uploaded picture in storage to reload it in case of
            validation error (to avoid the user to have to re-upload it). """
            values = super(MuskathlonRegistrationForm,
                           self).form_get_request_values()
            fname = 'ambassador_picture_1'
            image = values.get(fname)
            form_fields = self.form_fields()
            image_widget = form_fields[fname]['widget']
            if image:
                image_data = image_widget.form_to_binary(image)
                # Reset buffer image
                if hasattr(image, 'seek'):
                    image.seek(0)
                if image_data:
                    self.request.session[fname] = image_data
            else:
                image = self.request.session.get(fname)
                if image:
                    values[fname] = image
            return values

        def _form_load_sport_level_description(self, fname, field, value,
                                               **req_values):
            # Default value for event.registration field
            return req_values.get('sport_level_description', '')

        def _form_load_event_id(self, fname, field, value, **req_values):
            # Default value for event.registration field
            return int(req_values.get('event_id', self.event_id.id))

        def _form_validate_sport_level_description(self, value, **req_values):
            if not re.match(r"^[\w\s'-]+$", value, re.UNICODE):
                return 'sport_level_description', _(
                    'Please avoid any special characters')
            # No error
            return 0, 0

        def _form_validate_amount(self, value, **req_values):
            try:
                amount = float(value)
                if amount <= 0:
                    raise ValueError
            except ValueError:
                return 'amount', _('Please control the amount')
            except TypeError:
                # If amount is not defined, the event has no fee.
                return 0, 0
            # No error
            return 0, 0

        def form_before_create_or_update(self, values, extra_values):
            """ Create invoice for the registration.
            Create ambassador details.
            """
            super(MuskathlonRegistrationForm,
                  self).form_before_create_or_update(values, extra_values)
            uid = self.env.ref('muskathlon.user_muskathlon_portal').id
            partner = self.env['res.partner'].sudo().browse(
                values.get('partner_id')).exists()
            invoice_obj = self.env['account.invoice'].sudo(uid)
            invoice = invoice_obj
            event = self.event_id.sudo()
            if event.total_price:
                fee_template = self.env.ref('muskathlon.product_registration')
                product = fee_template.sudo(uid).product_variant_ids[:1]
                invoice = invoice_obj.create({
                    'partner_id':
                    partner.id,
                    'currency_id':
                    extra_values.get('currency_id'),
                    'origin':
                    'Muskathlon registration',
                    'invoice_line_ids': [(0, 0, {
                        'quantity':
                        1.0,
                        'price_unit':
                        event.total_price,
                        'account_analytic_id':
                        event.compassion_event_id.analytic_id.id,
                        'account_id':
                        product.property_account_income_id.id,
                        'name':
                        'Muskathlon registration fees',
                        'product_id':
                        product.id
                    })]
                })
            sporty = self.env.ref('partner_compassion.engagement_sport')
            if partner.advocate_details_id:
                partner.advocate_details_id.write({
                    'engagement_ids': [(4, sporty.id)],
                    't_shirt_size':
                    values.get('t_shirt_size')
                })
            else:
                # Creation of ambassador details reloads cache and remove
                # all field values in the form.
                # This hacks restores the form state after the creation.
                # backup = self._backup_fields()
                self.env['advocate.details'].sudo(uid).create({
                    'partner_id':
                    partner.id,
                    'advocacy_source':
                    'Online Muskathlon registration',
                    'engagement_ids': [(4, sporty.id)],
                    't_shirt_size':
                    values.get('t_shirt_size')
                })
                # self._restore_fields(backup)
            # Convert the name for event registration
            values['name'] = values.pop('partner_lastname', '')
            values['name'] += ' ' + values.pop('partner_firstname')
            # Force default value instead of setting 0.
            values.pop('amount_objective')
            # Parse integer
            values['event_id'] = int(values['event_id'])
            values['user_id'] = event.user_id.id
            values['stage_id'] = self.env.ref(
                'muskathlon.stage_down_payment').id
            # Store invoice and event for after form creation
            extra_values['invoice_id'] = invoice.id
            self.event_id = event

        def _form_create(self, values):
            uid = self.env.ref('muskathlon.user_muskathlon_portal').id
            # If notification is sent in same job, the form is reloaded
            # and all values are lost.
            main_object = self.form_model.sudo(uid).with_context(
                tracking_disable=True,
                registration_force_draft=True).create(values.copy())
            main_object.with_delay().notify_new_registration()
            self.main_object = main_object

        def form_next_url(self, main_object=None):
            # Clean storage of picture
            self.request.session.pop('ambassador_picture_1', False)
            if self.event_id.total_price:
                return super(MuskathlonRegistrationForm,
                             self).form_next_url(main_object)
            else:
                return '/muskathlon_registration/{}/success'.format(
                    self.main_object.id)

        def _edit_transaction_values(self, tx_values, form_vals):
            """ Add registration link and change reference. """
            tx_values.update({
                'registration_id':
                self.main_object.id,
                'reference':
                'MUSK-REG-' + str(self.main_object.id),
                'invoice_id':
                form_vals['invoice_id']
            })

        def _backup_fields(self):
            """ Hack method to save data the can be lost when environment
            is refreshed.
            :return: dict of values
            """
            return {
                'amount': self.amount,
                'currency_id': self.currency_id.id,
                'acquirer_id': self.acquirer_id.id,
                'partner_id': self.partner_id.id
            }

        def _restore_fields(self, backup):
            """Hack method to restore lost field values after environment
            refresh.
            :return: None
            """
            self.amount = backup['amount']
            self.currency_id = backup['currency_id']
            self.acquirer_id = backup['acquirer_id']
            self.partner_id = backup['partner_id']
class MailTracking(models.Model):
    _name = 'mail.tracking.value'
    _description = 'Mail Tracking Value'
    _rec_name = 'field'
    _order = 'tracking_sequence asc'

    field = fields.Many2one('ir.model.fields',
                            required=True,
                            readonly=1,
                            ondelete='cascade')
    field_desc = fields.Char('Field Description', required=True, readonly=1)
    field_type = fields.Char('Field Type')
    field_groups = fields.Char(compute='_compute_field_groups')

    old_value_integer = fields.Integer('Old Value Integer', readonly=1)
    old_value_float = fields.Float('Old Value Float', readonly=1)
    old_value_monetary = fields.Float('Old Value Monetary', readonly=1)
    old_value_char = fields.Char('Old Value Char', readonly=1)
    old_value_text = fields.Text('Old Value Text', readonly=1)
    old_value_datetime = fields.Datetime('Old Value DateTime', readonly=1)

    new_value_integer = fields.Integer('New Value Integer', readonly=1)
    new_value_float = fields.Float('New Value Float', readonly=1)
    new_value_monetary = fields.Float('New Value Monetary', readonly=1)
    new_value_char = fields.Char('New Value Char', readonly=1)
    new_value_text = fields.Text('New Value Text', readonly=1)
    new_value_datetime = fields.Datetime('New Value Datetime', readonly=1)

    currency_id = fields.Many2one(
        'res.currency',
        'Currency',
        readonly=True,
        ondelete='set null',
        help="Used to display the currency when tracking monetary values")

    mail_message_id = fields.Many2one('mail.message',
                                      'Message ID',
                                      required=True,
                                      index=True,
                                      ondelete='cascade')

    tracking_sequence = fields.Integer('Tracking field sequence',
                                       readonly=1,
                                       default=100)

    def _compute_field_groups(self):
        for tracking in self:
            model = self.env[tracking.mail_message_id.model]
            field = model._fields.get(tracking.field.name)
            tracking.field_groups = field.groups if field else 'base.group_system'

    @api.model
    def create_tracking_values(self, initial_value, new_value, col_name,
                               col_info, tracking_sequence, model_name):
        tracked = True

        field = self.env['ir.model.fields']._get(model_name, col_name)
        if not field:
            return

        values = {
            'field': field.id,
            'field_desc': col_info['string'],
            'field_type': col_info['type'],
            'tracking_sequence': tracking_sequence
        }

        if col_info['type'] in [
                'integer', 'float', 'char', 'text', 'datetime', 'monetary'
        ]:
            values.update({
                'old_value_%s' % col_info['type']: initial_value,
                'new_value_%s' % col_info['type']: new_value
            })
        elif col_info['type'] == 'date':
            values.update({
                'old_value_datetime':
                initial_value and fields.Datetime.to_string(
                    datetime.combine(fields.Date.from_string(initial_value),
                                     datetime.min.time())) or False,
                'new_value_datetime':
                new_value and fields.Datetime.to_string(
                    datetime.combine(fields.Date.from_string(new_value),
                                     datetime.min.time())) or False,
            })
        elif col_info['type'] == 'boolean':
            values.update({
                'old_value_integer': initial_value,
                'new_value_integer': new_value
            })
        elif col_info['type'] == 'selection':
            values.update({
                'old_value_char':
                initial_value and dict(col_info['selection'])[initial_value]
                or '',
                'new_value_char':
                new_value and dict(col_info['selection'])[new_value] or ''
            })
        elif col_info['type'] == 'many2one':
            values.update({
                'old_value_integer':
                initial_value and initial_value.id or 0,
                'new_value_integer':
                new_value and new_value.id or 0,
                'old_value_char':
                initial_value and initial_value.sudo().name_get()[0][1] or '',
                'new_value_char':
                new_value and new_value.sudo().name_get()[0][1] or ''
            })
        else:
            tracked = False

        if tracked:
            return values
        return {}

    def get_display_value(self, type):
        assert type in ('new', 'old')
        result = []
        for record in self:
            if record.field_type in [
                    'integer', 'float', 'char', 'text', 'monetary'
            ]:
                result.append(
                    getattr(record, '%s_value_%s' % (type, record.field_type)))
            elif record.field_type == 'datetime':
                if record['%s_value_datetime' % type]:
                    new_datetime = getattr(record, '%s_value_datetime' % type)
                    result.append('%sZ' % new_datetime)
                else:
                    result.append(record['%s_value_datetime' % type])
            elif record.field_type == 'date':
                if record['%s_value_datetime' % type]:
                    new_date = record['%s_value_datetime' % type]
                    result.append(fields.Date.to_string(new_date))
                else:
                    result.append(record['%s_value_datetime' % type])
            elif record.field_type == 'boolean':
                result.append(bool(record['%s_value_integer' % type]))
            else:
                result.append(record['%s_value_char' % type])
        return result

    def get_old_display_value(self):
        # grep : # old_value_integer | old_value_datetime | old_value_char
        return self.get_display_value('old')

    def get_new_display_value(self):
        # grep : # new_value_integer | new_value_datetime | new_value_char
        return self.get_display_value('new')
Exemplo n.º 20
0
class OtherMoneyOrder(models.Model):
    _name = 'other.money.order'
    _description = u'其他收入/其他支出'
    _inherit = ['mail.thread']

    TYPE_SELECTION = [
        ('other_pay', u'其他支出'),
        ('other_get', u'其他收入'),
    ]

    @api.model
    def create(self, values):
        # 创建单据时,更新订单类型的不同,生成不同的单据编号
        if self.env.context.get('type') == 'other_get':
            values.update({
                'name':
                self.env['ir.sequence'].next_by_code('other.get.order')
            })
        if self.env.context.get('type') == 'other_pay' or values.get(
                'name', '/') == '/':
            values.update({
                'name':
                self.env['ir.sequence'].next_by_code('other.pay.order')
            })

        return super(OtherMoneyOrder, self).create(values)

    @api.one
    @api.depends('line_ids.amount', 'line_ids.tax_amount')
    def _compute_total_amount(self):
        # 计算应付金额/应收金额
        self.total_amount = sum(
            (line.amount + line.tax_amount) for line in self.line_ids)

    state = fields.Selection([
        ('draft', u'草稿'),
        ('done', u'已确认'),
        ('cancel', u'已作废'),
    ],
                             string=u'状态',
                             readonly=True,
                             default='draft',
                             copy=False,
                             index=True,
                             help=u'其他收支单状态标识,新建时状态为草稿;确认后状态为已确认')
    partner_id = fields.Many2one('partner',
                                 string=u'往来单位',
                                 readonly=True,
                                 ondelete='restrict',
                                 states={'draft': [('readonly', False)]},
                                 help=u'单据对应的业务伙伴,单据确认时会影响他的应收应付余额')
    date = fields.Date(string=u'单据日期',
                       readonly=True,
                       default=lambda self: fields.Date.context_today(self),
                       states={'draft': [('readonly', False)]},
                       copy=False,
                       help=u'单据创建日期')
    name = fields.Char(string=u'单据编号',
                       copy=False,
                       readonly=True,
                       default='/',
                       help=u'单据编号,创建时会根据类型自动生成')
    total_amount = fields.Float(string=u'金额',
                                compute='_compute_total_amount',
                                store=True,
                                readonly=True,
                                digits=dp.get_precision('Amount'),
                                help=u'本次其他收支的总金额')
    bank_id = fields.Many2one('bank.account',
                              string=u'结算账户',
                              required=True,
                              ondelete='restrict',
                              readonly=True,
                              states={'draft': [('readonly', False)]},
                              help=u'本次其他收支的结算账户')
    line_ids = fields.One2many('other.money.order.line',
                               'other_money_id',
                               string=u'收支单行',
                               readonly=True,
                               copy=True,
                               states={'draft': [('readonly', False)]},
                               help=u'其他收支单明细行')
    type = fields.Selection(TYPE_SELECTION,
                            string=u'类型',
                            readonly=True,
                            default=lambda self: self._context.get('type'),
                            states={'draft': [('readonly', False)]},
                            help=u'类型:其他收入 或者 其他支出')
    note = fields.Text(u'备注', help=u'可以为该单据添加一些需要的标识信息')

    is_init = fields.Boolean(u'初始化应收应付', help=u'此单是否为初始化单')
    company_id = fields.Many2one(
        'res.company',
        string=u'公司',
        change_default=True,
        default=lambda self: self.env['res.company']._company_default_get())
    receiver = fields.Char(u'收款人', help=u'收款人')
    bank_name = fields.Char(u'开户行')
    bank_num = fields.Char(u'银行账号')
    voucher_id = fields.Many2one('voucher',
                                 u'对应凭证',
                                 readonly=True,
                                 ondelete='restrict',
                                 copy=False,
                                 help=u'其他收支单确认时生成的对应凭证')
    currency_amount = fields.Float(u'外币金额', digits=dp.get_precision('Amount'))

    @api.onchange('date')
    def onchange_date(self):
        if self._context.get('type') == 'other_get':
            return {'domain': {'partner_id': [('c_category_id', '!=', False)]}}
        else:
            return {'domain': {'partner_id': [('s_category_id', '!=', False)]}}

    @api.onchange('partner_id')
    def onchange_partner_id(self):
        """
        更改业务伙伴,自动填入收款人、开户行和银行帐号
        """
        if self.partner_id:
            self.receiver = self.partner_id.name
            self.bank_name = self.partner_id.bank_name
            self.bank_num = self.partner_id.bank_num

    @api.multi
    def other_money_done(self):
        '''其他收支单的审核按钮'''
        self.ensure_one()
        if float_compare(self.total_amount, 0, 3) <= 0:
            raise UserError(u'金额应该大于0。\n金额:%s' % self.total_amount)
        if not self.bank_id.account_id:
            raise UserError(u'请配置%s的会计科目' % (self.bank_id.name))

        # 根据单据类型更新账户余额
        if self.type == 'other_pay':
            decimal_amount = self.env.ref('core.decimal_amount')
            if float_compare(self.bank_id.balance, self.total_amount,
                             decimal_amount.digits) == -1:
                raise UserError(u'账户余额不足。\n账户余额:%s 本次支出金额:%s' %
                                (self.bank_id.balance, self.total_amount))
            self.bank_id.balance -= self.total_amount
        else:
            self.bank_id.balance += self.total_amount

        # 创建凭证并审核非初始化凭证
        vouch_obj = self.create_voucher()
        return self.write({
            'voucher_id': vouch_obj.id,
            'state': 'done',
        })

    @api.multi
    def other_money_draft(self):
        '''其他收支单的反审核按钮'''
        self.ensure_one()
        # 根据单据类型更新账户余额
        if self.type == 'other_pay':
            self.bank_id.balance += self.total_amount
        else:
            decimal_amount = self.env.ref('core.decimal_amount')
            if float_compare(self.bank_id.balance, self.total_amount,
                             decimal_amount.digits) == -1:
                raise UserError(u'账户余额不足。\n账户余额:%s 本次支出金额:%s' %
                                (self.bank_id.balance, self.total_amount))
            self.bank_id.balance -= self.total_amount

        voucher = self.voucher_id
        self.write({
            'voucher_id': False,
            'state': 'draft',
        })
        # 反审核凭证并删除
        if voucher.state == 'done':
            voucher.voucher_draft()
        # 始初化单反审核只删除明细行
        if self.is_init:
            vouch_obj = self.env['voucher'].search([('id', '=', voucher.id)])
            vouch_obj_lines = self.env['voucher.line'].search([
                ('voucher_id', '=', vouch_obj.id),
                ('account_id', '=', self.bank_id.account_id.id),
                ('init_obj', '=', 'other_money_order-%s' % (self.id))
            ])
            for vouch_obj_line in vouch_obj_lines:
                vouch_obj_line.unlink()
        else:
            voucher.unlink()
        return True

    @api.multi
    def create_voucher(self):
        """创建凭证并审核非初始化凭证"""
        init_obj = ''
        # 初始化单的话,先找是否有初始化凭证,没有则新建一个
        if self.is_init:
            vouch_obj = self.env['voucher'].search([('is_init', '=', True)])
            if not vouch_obj:
                vouch_obj = self.env['voucher'].create({
                    'date':
                    self.date,
                    'is_init':
                    True,
                    'ref':
                    '%s,%s' % (self._name, self.id)
                })
        else:
            vouch_obj = self.env['voucher'].create({
                'date':
                self.date,
                'ref':
                '%s,%s' % (self._name, self.id)
            })
        if self.is_init:
            init_obj = 'other_money_order-%s' % (self.id)

        if self.type == 'other_get':  # 其他收入单
            self.other_get_create_voucher_line(vouch_obj, init_obj)
        else:  # 其他支出单
            self.other_pay_create_voucher_line(vouch_obj)

        # 如果非初始化单则审核
        if not self.is_init:
            vouch_obj.voucher_done()
        return vouch_obj

    def other_get_create_voucher_line(self, vouch_obj, init_obj):
        """
        其他收入单生成凭证明细行
        :param vouch_obj: 凭证
        :return:
        """
        vals = {}
        for line in self.line_ids:
            if not line.category_id.account_id:
                raise UserError(u'请配置%s的会计科目' % (line.category_id.name))

            rate_silent = self.env['res.currency'].get_rate_silent(
                self.date, self.bank_id.currency_id.id)
            vals.update({
                'vouch_obj_id': vouch_obj.id,
                'name': self.name,
                'note': line.note or '',
                'credit_auxiliary_id': line.auxiliary_id.id,
                'amount': abs(line.amount + line.tax_amount),
                'credit_account_id': line.category_id.account_id.id,
                'debit_account_id': self.bank_id.account_id.id,
                'partner_credit': self.partner_id.id,
                'partner_debit': '',
                'sell_tax_amount': line.tax_amount or 0,
                'init_obj': init_obj,
                'currency_id': self.bank_id.currency_id.id,
                'currency_amount': self.currency_amount,
                'rate_silent': rate_silent,
            })
            # 贷方行
            if not init_obj:
                self.env['voucher.line'].create({
                    'name':
                    u"%s %s" % (vals.get('name'), vals.get('note')),
                    'partner_id':
                    vals.get('partner_credit', ''),
                    'account_id':
                    vals.get('credit_account_id'),
                    'credit':
                    line.amount,
                    'voucher_id':
                    vals.get('vouch_obj_id'),
                    'auxiliary_id':
                    vals.get('credit_auxiliary_id', False),
                })
            # 销项税行
            if vals.get('sell_tax_amount'):
                if not self.env.user.company_id.output_tax_account:
                    raise UserError(
                        u'您还没有配置公司的销项税科目。\n请通过"配置-->高级配置-->公司"菜单来设置销项税科目!')
                self.env['voucher.line'].create({
                    'name':
                    u"%s %s" % (vals.get('name'), vals.get('note')),
                    'account_id':
                    self.env.user.company_id.output_tax_account.id,
                    'credit':
                    line.tax_amount or 0,
                    'voucher_id':
                    vals.get('vouch_obj_id'),
                })
        # 借方行
        self.env['voucher.line'].create({
            'name':
            u"%s" % (vals.get('name')),
            'account_id':
            vals.get('debit_account_id'),
            'debit':
            self.total_amount,  # 借方和
            'voucher_id':
            vals.get('vouch_obj_id'),
            'partner_id':
            vals.get('partner_debit', ''),
            'auxiliary_id':
            vals.get('debit_auxiliary_id', False),
            'init_obj':
            vals.get('init_obj', False),
            'currency_id':
            vals.get('currency_id', False),
            'currency_amount':
            vals.get('currency_amount'),
            'rate_silent':
            vals.get('rate_silent'),
        })

    def other_pay_create_voucher_line(self, vouch_obj):
        """
        其他支出单生成凭证明细行
        :param vouch_obj: 凭证
        :return:
        """
        vals = {}
        for line in self.line_ids:
            if not line.category_id.account_id:
                raise UserError(u'请配置%s的会计科目' % (line.category_id.name))

            rate_silent = self.env['res.currency'].get_rate_silent(
                self.date, self.bank_id.currency_id.id)
            vals.update({
                'vouch_obj_id': vouch_obj.id,
                'name': self.name,
                'note': line.note or '',
                'debit_auxiliary_id': line.auxiliary_id.id,
                'amount': abs(line.amount + line.tax_amount),
                'credit_account_id': self.bank_id.account_id.id,
                'debit_account_id': line.category_id.account_id.id,
                'partner_credit': '',
                'partner_debit': self.partner_id.id,
                'buy_tax_amount': line.tax_amount or 0,
                'currency_id': self.bank_id.currency_id.id,
                'currency_amount': self.currency_amount,
                'rate_silent': rate_silent,
            })
            # 借方行
            self.env['voucher.line'].create({
                'name':
                u"%s %s " % (vals.get('name'), vals.get('note')),
                'account_id':
                vals.get('debit_account_id'),
                'debit':
                line.amount,
                'voucher_id':
                vals.get('vouch_obj_id'),
                'partner_id':
                vals.get('partner_debit', ''),
                'auxiliary_id':
                vals.get('debit_auxiliary_id', False),
                'init_obj':
                vals.get('init_obj', False),
            })
            # 进项税行
            if vals.get('buy_tax_amount'):
                if not self.env.user.company_id.import_tax_account:
                    raise UserError(u'请通过"配置-->高级配置-->公司"菜单来设置进项税科目')
                self.env['voucher.line'].create({
                    'name':
                    u"%s %s" % (vals.get('name'), vals.get('note')),
                    'account_id':
                    self.env.user.company_id.import_tax_account.id,
                    'debit':
                    line.tax_amount or 0,
                    'voucher_id':
                    vals.get('vouch_obj_id'),
                })
        # 贷方行
        self.env['voucher.line'].create({
            'name':
            u"%s" % (vals.get('name')),
            'partner_id':
            vals.get('partner_credit', ''),
            'account_id':
            vals.get('credit_account_id'),
            'credit':
            self.total_amount,  # 贷方和
            'voucher_id':
            vals.get('vouch_obj_id'),
            'auxiliary_id':
            vals.get('credit_auxiliary_id', False),
            'init_obj':
            vals.get('init_obj', False),
            'currency_id':
            vals.get('currency_id', False),
            'currency_amount':
            vals.get('currency_amount'),
            'rate_silent':
            vals.get('rate_silent'),
        })
Exemplo n.º 21
0
class OeHealthPrescriptions(models.Model):
    _name = 'oeh.medical.prescription'
    _description = 'Prescriptions'

    STATES = [
        ('Draft', 'Draft'),
        ('Invoiced', 'Invoiced'),
        ('Sent to Pharmacy', 'Sent to Pharmacy'),
    ]

    # Automatically detect logged in physician
    @api.multi
    def _get_physician(self):
        """Return default physician value"""
        therapist_obj = self.env['oeh.medical.physician']
        domain = [('oeh_user_id', '=', self.env.uid)]
        user_ids = therapist_obj.search(domain, limit=1)
        if user_ids:
            return user_ids.id or False
        else:
            return False

    name = fields.Char(string='Prescription #',
                       size=64,
                       readonly=True,
                       required=True,
                       default=lambda *a: '/')
    patient = fields.Many2one('oeh.medical.patient',
                              string='Patient',
                              help="Patient Name",
                              required=True,
                              readonly=True,
                              states={'Draft': [('readonly', False)]})
    doctor = fields.Many2one('oeh.medical.physician',
                             string='Physician',
                             domain=[('is_pharmacist', '=', False)],
                             help="Current primary care / family doctor",
                             required=True,
                             readonly=True,
                             states={'Draft': [('readonly', False)]},
                             default=_get_physician)
    pharmacy = fields.Many2one('oeh.medical.health.center.pharmacy',
                               'Pharmacy',
                               readonly=True,
                               states={'Draft': [('readonly', False)]})
    date = fields.Datetime(string='Prescription Date',
                           readonly=True,
                           states={'Draft': [('readonly', False)]},
                           default=datetime.datetime.now())
    info = fields.Text(string='Prescription Notes',
                       readonly=True,
                       states={'Draft': [('readonly', False)]})
    prescription_line = fields.One2many(
        'oeh.medical.prescription.line',
        'prescription_id',
        string='Prescription Lines',
        readonly=True,
        states={'Draft': [('readonly', False)]})
    state = fields.Selection(STATES,
                             'State',
                             readonly=True,
                             default=lambda *a: 'Draft')

    @api.model
    def create(self, vals):
        sequence = self.env['ir.sequence'].next_by_code(
            'oeh.medical.prescription')
        vals['name'] = sequence
        health_prescription = super(OeHealthPrescriptions, self).create(vals)
        return health_prescription

    @api.multi
    def _default_account(self):
        journal = self.env['account.journal'].search([('type', '=', 'sale')],
                                                     limit=1)
        return journal.default_credit_account_id.id

    def action_prescription_invoice_create(self):
        invoice_obj = self.env["account.invoice"]
        invoice_line_obj = self.env["account.invoice.line"]
        inv_ids = []

        for pres in self:
            # Create Invoice
            if pres.patient:
                curr_invoice = {
                    'partner_id':
                    pres.patient.partner_id.id,
                    'account_id':
                    pres.patient.partner_id.property_account_receivable_id.id,
                    'patient':
                    pres.patient.id,
                    'state':
                    'draft',
                    'type':
                    'out_invoice',
                    'date_invoice':
                    pres.date.strftime('%Y-%m-%d'),
                    'origin':
                    "Prescription# : " + pres.name,
                    'sequence_number_next_prefix':
                    False
                }

                inv_ids = invoice_obj.create(curr_invoice)
                inv_id = inv_ids.id

                if inv_ids:
                    prd_account_id = self._default_account()
                    if pres.prescription_line:
                        for ps in pres.prescription_line:

                            # Create Invoice line
                            curr_invoice_line = {
                                'name': ps.name.product_id.name,
                                'product_id': ps.name.product_id.id,
                                'price_unit': ps.name.product_id.list_price,
                                'quantity': ps.qty,
                                'account_id': prd_account_id,
                                'invoice_id': inv_id,
                            }

                            inv_line_ids = invoice_line_obj.create(
                                curr_invoice_line)

                self.write({'state': 'Invoiced'})

        return {
            'domain': "[('id','=', " + str(inv_id) + ")]",
            'name': 'Prescription Invoice',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'account.invoice',
            'type': 'ir.actions.act_window'
        }

    @api.multi
    def unlink(self):
        for priscription in self.filtered(
                lambda priscription: priscription.state not in ['Draft']):
            raise UserError(
                _('You can not delete a prescription which is not in "Draft" state !!'
                  ))
        return super(OeHealthPrescriptions, self).unlink()

    def action_prescription_send_to_pharmacy(self):
        pharmacy_obj = self.env["oeh.medical.health.center.pharmacy.line"]
        pharmacy_line_obj = self.env[
            "oeh.medical.health.center.pharmacy.prescription.line"]
        res = {}
        for pres in self:
            if not pres.pharmacy:
                raise UserError(_('No pharmacy selected !!'))
            else:
                curr_pres = {
                    'name': pres.id,
                    'patient': pres.patient.id,
                    'doctor': pres.doctor.id,
                    'pharmacy_id': pres.pharmacy.id,
                }
                phy_ids = pharmacy_obj.create(curr_pres)

                if phy_ids:
                    if pres.prescription_line:
                        for ps in pres.prescription_line:

                            # Create Prescription line
                            curr_pres_line = {
                                'name': ps.name.id,
                                'indication': ps.indication.id,
                                'price_unit': ps.name.product_id.list_price,
                                'qty': ps.qty,
                                'actual_qty': ps.qty,
                                'prescription_id': phy_ids.id,
                                'state': 'Draft',
                            }

                            phy_line_ids = pharmacy_line_obj.create(
                                curr_pres_line)

                res = self.write({'state': 'Sent to Pharmacy'})

        return True

    @api.multi
    def print_patient_prescription(self):
        return self.env.ref(
            'oehealth.action_oeh_medical_report_patient_prescriptions'
        ).report_action(self)
Exemplo n.º 22
0
class HrEmployee(models.Model):
    _inherit = 'hr.employee'

    # name = fields.Char(string="Employee Name", store=True, readonly=False, tracking=True)
    # user_id = fields.Many2one('res.users', 'User', store=True, readonly=False)
    # active = fields.Boolean('Active', default=True, store=True, readonly=False)
    #
    #
    # #header
    # employee_type = fields.Selection([('regular','Regular Employee'),
    #                                  ('contractual_with_agency','Contractual with Agency'),
    #                                  ('contractual_with_stpi','Contractual with STPI')],string='Employment Type',track_visibility='always', store=True)
    #
    # recruitment_type = fields.Selection([
    #                                 ('d_recruitment','Direct Recruitment(DR)'),
    #                                 ('transfer','Transfer(Absorption)'),
    #                                 ('i_absorption','Immediate Absorption'),
    #                                 ('deputation','Deputation'),
    #                                 ('c_appointment','Compassionate Appointment'),
    #                                 ('promotion','Promotion'),
    #                                      ],'Recruitment Type',track_visibility='always', store=True)
    #
    # salutation = fields.Many2one('res.partner.title',track_visibility='always')
    #
    # fax_number = fields.Char('FAX number',track_visibility='always')
    #
    # #added by Sangita
    # pay_level = fields.Many2one('payslip.pay.level', string='Pay Band')

    #citizenship
    # citizen_country_id = fields.Many2one('res.country','Country name')


    # def default_country(self):
    #     return self.env['res.country'].search([('name', '=', 'India')], limit=1)
    #
    # country_id = fields.Many2one(
    #     'res.country', 'Nationality (Country)', groups="hr.group_hr_user", default=default_country)
    # citizen_number = fields.Char('Citizen Number',track_visibility='always')
    # citizen_eligibility_date =fields.Date(string='Date of Eligibility',track_visibility='always')
    # citizen_file_data = fields.Binary('Upload',track_visibility='always')
    # date_of_eligibility = fields.Date('Date of Eligibility', track_visibility='always')
    # citizen_file_name = fields.Char('File Name',track_visibility='always')
    # show_citizen_field = fields.Boolean('Show Field',default=False,copy=False,track_visibility='always')
    #
    # #religion
    # category = fields.Many2one('employee.category',string='Category',track_visibility='always')
    # religion = fields.Many2one('employee.religion',string='Religion',track_visibility='always')
    # minority = fields.Boolean('Minority',default=False,track_visibility='always')
    #
    #  #office work
    # # gender = fields.Selection(selection_add=[('transgender', 'Transgender')])
    # gende = fields.Selection([
    #     ('male', 'Male'),
    #     ('female', 'Female'),
    #     ('transgender', 'Transgender')
    #                           ], string='Gender',track_visibility='always')
    # recruitment_file_no = fields.Char('Recruitment File No.',track_visibility='always')
    # office_file_no = fields.Char('Office Order No.',track_visibility='always')
    # mode_of_recruitment = fields.Char('Mode Of Recruitment',track_visibility='always')
    # post = fields.Char('Post',track_visibility='always')
    # date_of_join = fields.Date('Date of Joining',track_visibility='always')
    # office_order_date = fields.Date('Office Order Date',track_visibility='always')
    #
    # #contact
    # personal_email =fields.Char('Personal Email',track_visibility='always')
    # phone = fields.Char('Phone (Home)',track_visibility='always')
    #
    # #work_infroamtion
    # ex_serviceman =fields.Selection([('no','No'),
    #                                  ('yes','Yes')],string='Whether Ex Service Man',track_visibility='always')
    #
    # #physical
    # height = fields.Float('Height (in CMs)',track_visibility='always')
    # weight = fields.Float('Weight (in KGs)',track_visibility='always')
    # blood_group = fields.Selection([('a+','A+'),
    #                                 ('a1+','A1+'),
    #                                  ('a-','A-'),
    #                                  ('b+','B+'),
    #                                  ('b-','B-'),
    #                                  ('o+', 'O+'),
    #                                  ('o-', 'O-'),
    #                                  ('ab+','AB+'),
    #                                  ('ab-','AB-')],string='Blood Group',track_visibility='always')
    # differently_abled = fields.Selection([('no','No'),
    #                                       ('yes','Yes')], default = 'no', string='Differently Abled?',track_visibility='always')
    # kind_of_disability = fields.Selection([('vh', 'No'),
    #                                        ('hh', 'Yes'),
    #                                        ('ph', 'Yes')], string='Kind of Disability',
    #                                       track_visibility='always')
    # perc_disability = fields.Char('% of Disability',track_visibility='always')
    # certificate_upload = fields.Binary('Upload certificate',track_visibility='always')
    # personal_remark =fields.Char('Personal mark of Identification',track_visibility='always')
    #
    #
    #
    # #Identification
    # identify_id = fields.Char(string='Identification No.',copy=False, store=True, track_visibility='always', compute='_compute_identify_no')
    # pan_no = fields.Char('PAN Card No.',track_visibility='always')
    # uan_no = fields.Char('UAN No.',track_visibility='always')
    # pan_upload = fields.Binary('Upload(PAN)',track_visibility='always')
    # aadhar_no = fields.Char('Aadhar Card No.',track_visibility='always')
    # aadhar_upload = fields.Binary('Upload(Aadhar)',track_visibility='always')
    # passport_upload = fields.Binary('Upload(Passport)',track_visibility='always')
    # bank_name = fields.Char(string='Bank Name')
    # bank_account_number = fields.Char(string='Bank Account number')
    # ifsc_code = fields.Char(string='IFSC Code')
    #
    # _sql_constraints = [
    #     ('pan_uniq', 'unique (pan_no)', 'Pan No must be unique!'),
    #     ('aadhar_uniq', 'unique (aadhar_no)', 'Aadhar no must be unique!'),
    #     ('passport_uniq', 'unique (passport_id)', 'Passport no must be unique!'),
    # ]
    #

    # category_ids = fields.Many2many('hr.employee.category', 'employee_category_rel', 'emp_id', 'category_id', 'Tags', required=False)




    # @api.constrains('mobile_phone','work_phone','phone')
    # @api.onchange('mobile_phone','work_phone','phone')
    # def _check_mobile_phone_num(self):
    #     for rec in self:
    #         if rec.mobile_phone and not rec.mobile_phone.isnumeric():
    #             raise ValidationError(_("Phone number must be a number"))
    #         if rec.mobile_phone and len(rec.mobile_phone) != 10:
    #             raise ValidationError(_("Please enter correct Mobile number."
    #                                     "It must be of 10 digits"))
    #         if rec.work_phone and not rec.work_phone.isnumeric():
    #             raise ValidationError(_("Phone number must be a number"))
    #         if rec.work_phone and len(rec.work_phone) != 10:
    #             raise ValidationError(_("Please enter correct work phone number."
    #                                     "It must be of 10 digits"))
    #         if rec.phone and not rec.phone.isnumeric():
    #             raise ValidationError(_("Phone number must be a number"))
    #         if rec.phone and len(rec.phone) != 10:
    #             raise ValidationError(_("Please enter correct phone number."
    #                                             "It must be of 10 digits"))


    @api.constrains('personal_email')
    def check_unique_personal_email(self):
        for rec in self:
            count = 0
            emp_id = self.env['hr.employee'].sudo().search(
                [('personal_email', '=', rec.personal_email)])
            for e in emp_id:
                count += 1
            if count > 1:
                raise ValidationError("The Personal Email must be unique")

    # @api.constrains('work_email')
    # def check_unique_work_email(self):
    #     for rec in self:
    #         count = 0
    #         emp_id = self.env['hr.employee'].sudo().search(
    #             [('work_email', '=', rec.work_email)])
    #         for e in emp_id:
    #             count += 1
    #         if count > 1:
    #             raise ValidationError("The Work Email must be unique")

    #
    #
    #
    # @api.constrains('work_email')
    # def _check_work_mail_val(self):
    #     for employee in self:
    #         regex = '^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$'
    #         if not (re.search(regex, employee.work_email)):
    #             raise ValidationError(_('Please enter correct Work Mail Address.'))
    #

    @api.onchange('branch_id')
    @api.constrains('branch_id')
    def get_partner_from_branch(self):
        for rec in self:
            if rec.branch_id.partner_id:
                rec.address_id = rec.branch_id.partner_id.id

    # @api.constrains('name')
    # @api.onchange('name')
    # def _check_name_validation(self):
    #     for rec in self:
    #         if rec.name:
    #             for e in rec.name:
    #                 if not(e.isalpha() or e == ' '):
    #                     raise ValidationError(_("Please enter correct Name."))
    #


    # added by sangita
    def get_document_ids(self):

        for document in self:
            document_ids = self.env['hr.employee.document'].sudo().search([('employee_ref', '=', document.id)])
            print("?????????????????????????", document_ids)
        #             for doc in document_ids:

        return document_ids

    def get_leave_record(self):
        for leave in self:
            if leave.id:
                SQL = """
                           select hla.create_date as date,
                            hla.number_of_days as days,
                            hla.holiday_status_id as holiday
                            from hr_leave_allocation as hla 
                            inner join hr_leave_type as hly on hly.id = hla.holiday_status_id
                            where employee_id = %s and state in ('validate') and holiday_type = 'employee'
                            group by
                            hla.id,
                            hla.employee_id,
                            hla.holiday_status_id
                        """
                self.env.cr.execute(SQL, (
                    leave.id,
                ))
                res = self.env.cr.fetchall()
                #                 r = [i for i in res]
                print("??????????????????????casual_leavescasual_leaves", res)
                return res

    def find_age(self):
        age = (date.today() - self.birthday) // timedelta(days=365.2425)
        #         print("?????????????????????????age",age)
        return age

    def relative_types(self):
        for relative in self:
            relativ_id = self.env['employee.relative'].search([('employee_id', '=', relative.id)])
            #             print("????????????fffffffffff???????????????",relativ_id)
            for rel_type in relativ_id:
                relative_type = rel_type.relative_type
        #                 print("relative_typerelative_typerelative_type",relative_type)
        return relative_type

    def reltive_details(self):
        for relative in self:
            if relative:
                SQL = """

                        select er.name,
                            rt.name,
                            ROUND(er.age) as roundage
                         from employee_relative as er
                            inner join hr_employee as he on he.id = er.employee_id
                            inner join relative_type as rt on rt.id = er.relate_type
                            where er.employee_id = %s
                    """
                self.env.cr.execute(SQL, (
                    relative.id,
                ))

                res = self.env.cr.fetchall()

                return res

    def get_ltc_record(self):
        for ltc in self:
            if ltc.id:
                SQL = """
                        select he.name as emp,
                            ela.hometown_address,
                            ela.el_encashment
                            from employee_ltc_advance as ela
                            inner join hr_employee as he on he.id = ela.employee_id
                            where ela.employee_id = %s

                            group by 
                            he.name,
                            ela.hometown_address,
                            ela.el_encashment

                    """
                self.env.cr.execute(SQL, (
                    ltc.id,
                ))

                res = self.env.cr.fetchall()

                return res

    def leave_available_balance(self):
        for leave in self:
            if leave:
                SQL = """
                        select hlr.holiday_status_id as holiday,
                            sum(hlr.number_of_days) as days 
                            from hr_leave_report hlr 
                            inner join hr_leave_type as hly on hly.id = hlr.holiday_status_id
                            where employee_id = %s and holiday_type = 'employee' and state not in ('refuse')
                            group by 
                            hlr.holiday_status_id
                    """

                self.env.cr.execute(SQL, (
                    leave.id,
                ))
                res = self.env.cr.fetchall()
                #                 r = [i for i in res]
                #                 print("??????????????????????casual_leavescasual_leaves",res)
                return res

    # @api.model
    # def create(self, vals):
    #     res = super(HrEmployee, self).create(vals)
    #     if res.employee_type == 'regular':
    #         seq = self.env['ir.sequence'].next_by_code('hr.employee')
    #         res.identify_id = 'STPI' + str(seq)
    #     else:
    #         seq = self.env['ir.sequence'].next_by_code('identify.seqid')
    #         res.identify_id = 'STPITEMP' + str(seq)
    #     return res

    @api.constrains('date_of_join')
    def get_joining_emp_code(self):
        for res in self:
            if res.employee_type == 'regular':
                seq = self.env['ir.sequence'].next_by_code('hr.employee')
                res.identify_id = 'STPI' + str(seq)
            else:
                seq = self.env['ir.sequence'].next_by_code('identify.seqid')
                res.identify_id = 'STPITEMP' + str(seq)


    #
    # @api.depends('employee_type')
    # def _compute_identify_no(self):
    #     for res in self:
    #         if res.identify_id == False:
    #             if res.employee_type == 'regular':
    #                 seq = self.env['ir.sequence'].next_by_code('hr.employee')
    #                 res.identify_id = 'STPI' + str(seq)
    #             else:
    #                 seq = self.env['ir.sequence'].next_by_code('identify.seqid')
    #                 res.identify_id = 'STPITEMP' + str(seq)

    @api.constrains('date_of_join', 'office_order_date')
    @api.onchange('date_of_join','office_order_date')
    def _check_office_order_date(self):
        for record in self:
            if record.office_order_date and record.date_of_join and (record.office_order_date > record.date_of_join):
                raise ValidationError("Date of Joining should always be greater then equals to Office Order Date")


    @api.constrains('bank_account_number')
    @api.onchange('bank_account_number')
    def _check_bank_acc_number(self):
        for rec in self:
            if rec.bank_account_number:
                for e in rec.bank_account_number:
                    if not e.isdigit():
                        raise ValidationError(_("Please enter correct Account number, it must be numeric..."))


    @api.constrains('aadhar_no')
    @api.onchange('aadhar_no')
    def _check_aadhar_number(self):
        for rec in self:
            if rec.aadhar_no:
                for e in rec.aadhar_no:
                    if not e.isdigit():
                        raise ValidationError(_("Please enter correct Aadhar number, it must be numeric..."))
                if len(rec.aadhar_no) != 12:
                    raise ValidationError(_("Please enter correct Aadhar number, it must be of 12 digits..."))


    @api.constrains('pan_no')
    @api.onchange('pan_no')
    def _check_pan_number(self):
        for rec in self:
            if rec.pan_no and not re.match(r'^[A-Za-z]{5}[0-9]{4}[A-Za-z]$', str(rec.pan_no)):
                raise ValidationError(_("Please enter correct PAN number..."))

    @api.constrains('birthday')
    def _check_birthday_app(self):
        for employee in self:
            today = datetime.now().date()
            if employee.birthday  and employee.birthday > today:
                raise ValidationError(_('Please enter correct date of birth'))



    @api.constrains('office_order_date')
    def _check_office_order_date_app(self):
        for employee in self:
            today = datetime.now().date()
            if employee.office_order_date and employee.office_order_date > today:
                raise ValidationError(_('Please enter correct office order date'))



    @api.onchange('pan_no')
    def set_upper(self):
        if self.pan_no:
            self.pan_no = str(self.pan_no).upper()
            




    # address_ids = fields.One2many('employee.address','employee_id',string='Address',track_visibility='always')

    #Personal File Detail
    file_no = fields.Char('File No',track_visibility='always')
    file_open_date = fields.Date('File Open Date',track_visibility='always')
    file_close_date = fields.Date('File close Date',track_visibility='always')
    file_remark = fields.Text('Remark',track_visibility='always')


    @api.onchange('country_id')
    def ckech_nationality(self):
        if self.country_id:
            if self.country_id.name != 'India':
                self.show_citizen_field =True
            else:
                self.show_citizen_field =False




    def set_employee_training(self):
        if self:
            return {
                'name': 'Employee Training',
                'view_type': 'form',
                'view_mode': 'tree',
                'res_model': 'employee.training',
                'type': 'ir.actions.act_window',
                'target': 'current',
                'view_id': self.env.ref('l10n_in_hr_fields.employee_training_tree_view').id,
                'domain': [('employee_id', '=', self.id)],
                'context':{
                        'default_employee_id': self.id}
            }

    # def set_last_employer(self):
    #     if self:
    #         return {
    #             'name': 'Last Employer',
    #             'view_type': 'form',
    #             'view_mode': 'tree',
    #             'res_model': 'employee.last_employer',
    #             'type': 'ir.actions.act_window',
    #             'target': 'current',
    #             'view_id': self.env.ref('l10n_in_hr_fields.employee_last_employer_tree_view').id,
    #             'domain': [('employee_id', '=', self.id)],
    #             'context':{
    #                     'default_employee_id': self.id}
    #         }

    # def set_employee_transfer(self):
    #     if self:
    #         return {
    #             'name': 'Hr Employee Transfer',
    #             'view_type': 'form',
    #             'view_mode': 'tree,form',
    #             'res_model': 'hr.employee.transfer',
    #             'type': 'ir.actions.act_window',
    #             'target': 'current',
    #             # 'view_id': self.env.ref('l10n_in_hr_fields.employeetransfer_form_view').id,
    #             'domain': [('employee_id', '=', self.id)],
    #             'context':{
    #                     'default_employee_id': self.id}
    #         }
    #



    # def get_family_detail(self):
    #     if self:
    #         return {
    #             'name': 'Family Details',
    #             'view_type': 'form',
    #             'view_mode': 'tree',
    #             'res_model': 'employee.relative',
    #             'type': 'ir.actions.act_window',
    #             'target': 'current',
    #             'view_id': self.env.ref('hr_applicant.view_employee_relative_tree').id,
    #             'domain': [('employee_id', '=', self.id)],
    #             'context': {
    #                 'default_employee_id': self.id}
    #             }

#
#
# class EmployeeAddress(models.Model):
#     _name = 'employee.address'
#     _description = 'Address'
#
#     def default_country(self):
#         return self.env['res.country'].search([('name', '=', 'India')], limit=1)
#
#     address_type = fields.Selection([('permanent_add', 'Permanent Add'),
#                                      ('present_add', 'Present Add'),
#                                      ('office_add', 'Office Add'),
#                                      ('hometown_add', 'HomeTown Add'),
#                                     ],string='Address Type',required=True)
#     employee_id = fields.Many2one('hr.employee','Employee Id')
#     street = fields.Char('Street')
#     street2 = fields.Char('Street2')
#     zip = fields.Char('Zip', change_default=True)
#     is_correspondence_address = fields.Boolean('Is Correspondence Address')
#     city = fields.Char('City')
#     state_id = fields.Many2one("res.country.state", string='State')
#     country_id = fields.Many2one('res.country', string='Country', default = default_country)
#     count = fields.Integer('Count')
#
#     @api.onchange('street', 'street2','zip', 'country_id','is_correspondence_address', 'city','state_id')
#     def _onchange_hometown_address(self):
#         for rec in self:
#             rec.count = 0
#             if rec.address_type == 'hometown_add':
#                 rec.count += 1
#             if rec.count >2:
#                 raise ValidationError("You cannot change Homettown address more than 2 times")
#
#
#     @api.constrains('address_type','employee_id')
#     def check_unique_add(self):
#         for rec in self:
#             count = 0
#             emp_id = self.env['employee.address'].search([('address_type', '=', rec.address_type),('employee_id', '=', rec.employee_id.id)])
#             for e in emp_id:
#                 count+=1
#             if count >1:
#                 raise ValidationError("The Address Type must be unique")
Exemplo n.º 23
0
class OeHealthPrescriptionLines(models.Model):
    _name = 'oeh.medical.prescription.line'
    _description = 'Prescription Lines'

    FREQUENCY_UNIT = [
        ('Seconds', 'Seconds'),
        ('Minutes', 'Minutes'),
        ('Hours', 'Hours'),
        ('Days', 'Days'),
        ('Weeks', 'Weeks'),
        ('When Required', 'When Required'),
    ]

    DURATION_UNIT = [
        ('Minutes', 'Minutes'),
        ('Hours', 'Hours'),
        ('Days', 'Days'),
        ('Months', 'Months'),
        ('Years', 'Years'),
        ('Indefinite', 'Indefinite'),
    ]

    prescription_id = fields.Many2one('oeh.medical.prescription',
                                      string='Prescription Reference',
                                      required=True,
                                      ondelete='cascade',
                                      index=True)
    name = fields.Many2one('oeh.medical.medicines',
                           string='Medicines',
                           help="Prescribed Medicines",
                           domain=[('medicament_type', '=', 'Medicine')],
                           required=True)
    indication = fields.Many2one(
        'oeh.medical.pathology',
        string='Indication',
        help=
        "Choose a disease for this medicament from the disease list. It can be an existing disease of the patient or a prophylactic."
    )
    dose = fields.Integer(
        string='Dose',
        help="Amount of medicines (eg, 250 mg ) each time the patient takes it"
    )
    dose_unit = fields.Many2one(
        'oeh.medical.dose.unit',
        string='Dose Unit',
        help="Unit of measure for the medication to be taken")
    dose_route = fields.Many2one(
        'oeh.medical.drug.route',
        string='Administration Route',
        help="HL7 or other standard drug administration route code.")
    dose_form = fields.Many2one('oeh.medical.drug.form',
                                'Form',
                                help="Drug form, such as tablet or gel")
    qty = fields.Integer(
        string='x',
        help="Quantity of units (eg, 2 capsules) of the medicament",
        default=lambda *a: 1.0)
    common_dosage = fields.Many2one(
        'oeh.medical.dosage',
        string='Frequency',
        help="Common / standard dosage frequency for this medicines")
    frequency = fields.Integer('Frequency')
    frequency_unit = fields.Selection(FREQUENCY_UNIT, 'Unit', index=True)
    admin_times = fields.Char(
        string='Admin hours',
        size=128,
        help=
        'Suggested administration hours. For example, at 08:00, 13:00 and 18:00 can be encoded like 08 13 18'
    )
    duration = fields.Integer(string='Treatment duration')
    duration_period = fields.Selection(
        DURATION_UNIT,
        string='Treatment period',
        help=
        "Period that the patient must take the medication. in minutes, hours, days, months, years or indefinately",
        index=True)
    start_treatment = fields.Datetime(string='Start of treatment')
    end_treatment = fields.Datetime('End of treatment')
    info = fields.Text('Comment')
    patient = fields.Many2one('oeh.medical.patient',
                              'Patient',
                              help="Patient Name")
Exemplo n.º 24
0
class File(models.Model):

    _name = 'muk_dms.file'
    _description = "File"
    _inherit = [
        'muk_dms.locking', 'muk_dms.access', 'mail.thread',
        'mail.activity.mixin'
    ]

    #----------------------------------------------------------
    # Database
    #----------------------------------------------------------

    name = fields.Char(string="Filename", required=True, index=True)

    active = fields.Boolean(
        string="Archived",
        default=True,
        help=
        "If a file is set to archived, it is not displayed, but still exists.")

    is_top_file = fields.Boolean(string="Top Directory",
                                 compute='_compute_is_top_file',
                                 search='_search_is_top_file')

    settings = fields.Many2one(comodel_name='muk_dms.settings',
                               string="Settings",
                               store=True,
                               auto_join=True,
                               ondelete='restrict',
                               compute='_compute_settings')

    show_tree = fields.Boolean(string="Show Structure",
                               related="settings.show_tree",
                               readonly=True)

    color = fields.Integer(string="Color")

    tags = fields.Many2many(comodel_name='muk_dms.tag',
                            relation='muk_dms_file_tag_rel',
                            column1='fid',
                            column2='tid',
                            string='Tags')

    category = fields.Many2one(string="Category",
                               comodel_name='muk_dms.category',
                               related="directory.category",
                               readonly=True,
                               store=True)

    content = fields.Binary(string='Content',
                            required=True,
                            compute='_compute_content',
                            inverse='_inverse_content')

    reference = fields.Reference(selection=[('muk_dms.data', _('Data'))],
                                 string="Data Reference",
                                 readonly=True)

    directory = fields.Many2one(comodel_name='muk_dms.directory',
                                string="Directory",
                                domain="[('permission_create', '=', True)]",
                                ondelete='restrict',
                                auto_join=True,
                                required=True)

    extension = fields.Char(string='Extension',
                            compute='_compute_extension',
                            readonly=True,
                            store=True)

    mimetype = fields.Char(string='Type',
                           compute='_compute_mimetype',
                           readonly=True,
                           store=True)

    size = fields.Integer(string='Size', readonly=True)

    path = fields.Char(string="Path",
                       store=True,
                       index=True,
                       readonly=True,
                       compute='_compute_path')

    relational_path = fields.Text(string="Path",
                                  store=True,
                                  readonly=True,
                                  compute='_compute_relational_path')

    custom_thumbnail = fields.Binary(string="Custom Thumbnail")

    thumbnail = fields.Binary(compute='_compute_thumbnail', string="Thumbnail")

    #----------------------------------------------------------
    # Functions
    #----------------------------------------------------------

    @api.multi
    def notify_change(self, values, *largs, **kwargs):
        super(File, self).notify_change(values, *largs, **kwargs)
        if "save_type" in values:
            self._update_reference_type()

    @api.multi
    def trigger_computation_up(self, fields, *largs, **kwargs):
        if ("no_trigger_computation_up" not in self.env.context):
            self.mapped('directory').suspend_security().with_context(
                is_parent=True).trigger_computation(fields)

    @api.multi
    def trigger_computation(self, fields, *largs, **kwargs):
        super(File, self).trigger_computation(fields,
                                              refresh=True,
                                              *largs,
                                              **kwargs)
        for record in self:
            values = {}
            if "settings" in fields:
                values.update(record._compute_settings(write=False))
            if "path" in fields:
                values.update(record._compute_path(write=False))
                values.update(record._compute_relational_path(write=False))
            if "extension" in fields:
                values.update(record._compute_extension(write=False))
            if "mimetype" in fields:
                values.update(record._compute_mimetype(write=False))
            if values:
                record.write(values)
                if "settings" in fields:
                    record.notify_change(
                        {'save_type': record.settings.save_type})

    @api.model
    def max_upload_size(self):
        params = self.env['ir.config_parameter'].sudo()
        return int(params.get_param('muk_dms.max_upload_size', default=25))

    @api.model
    def forbidden_extensions(self):
        params = self.env['ir.config_parameter'].sudo()
        forbidden_extensions = params.get_param('muk_dms.forbidden_extensions',
                                                default="")
        return [x.strip() for x in forbidden_extensions.split(',')]

    #----------------------------------------------------------
    # Search
    #----------------------------------------------------------

    @api.model
    def _search_permission_create(self, operator, operand):
        res = super(File, self)._search_permission_create(operator, operand)
        records = self.browse(
            res[0][2]).filtered(lambda r: r.directory.check_access('create'))
        if operator == '=' and operand:
            return [('id', 'in', records.mapped('id'))]
        return [('id', 'not in', records.mapped('id'))]

    @api.model
    def _search_is_top_file(self, operator, operand):
        files = self.search(
            []).filtered(lambda f: not f.directory.check_access('read'))
        if operator == '=' and operand:
            return [('id', 'in', files.mapped('id'))]
        return [('id', 'not in', files.mapped('id'))]

    #----------------------------------------------------------
    # Read, View
    #----------------------------------------------------------

    @api.multi
    def _compute_settings(self, write=True):
        if write:
            for record in self:
                record.settings = record.directory.settings
        else:
            self.ensure_one()
            return {'settings': self.directory.settings.id}

    @api.multi
    def _compute_extension(self, write=True):
        if write:
            for record in self:
                record.extension = os.path.splitext(record.name)[1]
        else:
            self.ensure_one()
            return {'extension': os.path.splitext(self.name)[1]}

    @api.multi
    def _compute_mimetype(self, write=True):
        def get_mimetype(record):
            mimetype = mimetypes.guess_type(record.name)[0]
            if (not mimetype or mimetype
                    == 'application/octet-stream') and record.content:
                mimetype = guess_mimetype(base64.b64decode(record.content))
            return mimetype or 'application/octet-stream'

        if write:
            for record in self:
                record.mimetype = get_mimetype(record)
        else:
            self.ensure_one()
            return {'mimetype': get_mimetype(self)}

    @api.multi
    def _compute_path(self, write=True):
        if write:
            for record in self:
                record.path = "%s%s" % (record.directory.path, record.name)
        else:
            self.ensure_one()
            return {'path': "%s%s" % (self.directory.path, self.name)}

    @api.multi
    def _compute_relational_path(self, write=True):
        def get_relational_path(record):
            path = json.loads(record.directory.relational_path)
            path.append({
                'model': record._name,
                'id': record.id,
                'name': record.name
            })
            return json.dumps(path)

        if write:
            for record in self:
                record.relational_path = get_relational_path(record)
        else:
            self.ensure_one()
            return {'relational_path': get_relational_path(self)}

    @api.multi
    def _compute_content(self):
        for record in self:
            record.content = record._get_content()

    @api.multi
    def _compute_permissions(self):
        super(File, self)._compute_permissions()
        for record in self:
            record.permission_create = record.permission_create and record.directory.check_access(
                'create')

    @api.depends('directory')
    def _compute_is_top_file(self):
        for record in self:
            record.is_top_file = record.directory.check_access('read')

    @api.depends('custom_thumbnail')
    def _compute_thumbnail(self):
        for record in self:
            if record.custom_thumbnail:
                record.thumbnail = record.with_context({}).custom_thumbnail
            else:
                extension = record.extension and record.extension.strip(
                    ".") or ""
                path = os.path.join(_img_path, "file_%s.png" % extension)
                if not os.path.isfile(path):
                    path = os.path.join(_img_path, "file_unkown.png")
                with open(path, "rb") as image_file:
                    record.thumbnail = base64.b64encode(image_file.read())

    #----------------------------------------------------------
    # Create, Update, Delete
    #----------------------------------------------------------

    @api.constrains('name')
    def _check_name(self):
        if not self.check_name(self.name):
            raise ValidationError(_("The file name is invalid."))
        childs = self.sudo().directory.files.mapped(
            lambda rec: [rec.id, rec.name])
        duplicates = [
            rec for rec in childs if rec[1] == self.name and rec[0] != self.id
        ]
        if duplicates:
            raise ValidationError(
                _("A file with the same name already exists."))

    @api.constrains('name')
    def _check_extension(self):
        forbidden_extensions = self.forbidden_extensions()
        file_extension = self._compute_extension(write=False)['extension']
        if file_extension and file_extension in forbidden_extensions:
            raise ValidationError(
                _("The file has a forbidden file extension."))

    @api.constrains('content')
    def _check_size(self):
        max_upload_size = self.max_upload_size()
        if (max_upload_size * 1024 * 1024) < len(base64.b64decode(
                self.content)):
            raise ValidationError(
                _("The maximum upload size is %s MB).") % max_upload_size)

    @api.constrains('directory')
    def _check_directory_access(self):
        for record in self:
            record.directory.check_access('create', raise_exception=True)

    @api.multi
    def _inverse_content(self):
        for record in self:
            if record.content:
                content = record.content
                size = len(base64.b64decode(content))
                reference = record.reference
                if reference:
                    record._update_reference_values({'content': content})
                    record.write({'size': size})
                else:
                    directory = record.directory
                    settings = record.settings if record.settings else directory.settings
                    reference = record._create_reference(
                        settings, directory.path, record.name, content)
                    reference = "%s,%s" % (reference._name, reference.id)
                    record.write({'reference': reference, 'size': size})
            else:
                record._unlink_reference()
                record.reference = None

    @api.multi
    def _before_unlink(self, *largs, **kwargs):
        info = super(File, self)._before_unlink(*largs, **kwargs)
        references = [
            list((k, list(map(lambda rec: rec.reference.id, v))))
            for k, v in itertools.groupby(
                self.sorted(key=lambda rec: rec.reference._name),
                lambda rec: rec.reference._name)
        ]
        info['references'] = references
        return info

    @api.multi
    def _after_unlink(self, result, info, infos, *largs, **kwargs):
        super(File, self)._after_unlink(result, info, infos, *largs, **kwargs)
        if 'references' in info and info['references']:
            for tuple in info['references']:
                self.env[tuple[0]].sudo().browse(list(filter(
                    None, tuple[1]))).unlink()

    @api.returns('self', lambda value: value.id)
    def copy(self, default=None):
        self.ensure_one()
        default = dict(default or [])
        names = []
        if 'directory' in default:
            directory = self.env['muk_dms.directory'].sudo().browse(
                default['directory'])
            names = directory.files.mapped('name')
        else:
            names = self.sudo().directory.files.mapped('name')
        default.update(
            {'name': self.unique_name(self.name, names, self.extension)})
        vals = self.copy_data(default)[0]
        if 'reference' in vals:
            del vals['reference']
        if not 'content' in vals:
            vals.update({'content': self.content})
        new = self.with_context(lang=None).create(vals)
        self.copy_translations(new)
        return new

    @api.multi
    def _check_recomputation(self, vals, olds, *largs, **kwargs):
        super(File, self)._check_recomputation(vals, olds, *largs, **kwargs)
        fields = []
        if 'name' in vals:
            fields.extend(['extension', 'mimetype', 'path'])
        if 'directory' in vals:
            fields.extend(['settings', 'path'])
        if 'content' in vals:
            fields.extend(['index_content'])
        if fields:
            self.trigger_computation(fields)
        self._check_reference_values(vals)

    #----------------------------------------------------------
    # Reference
    #----------------------------------------------------------

    @api.multi
    def _create_reference(self, settings, path, filename, content):
        self.ensure_one()
        self.check_access('create', raise_exception=True)
        if settings.save_type == 'database':
            return self.env['muk_dms.data_database'].sudo().create(
                {'data': content})
        return None

    @api.multi
    def _update_reference_values(self, values):
        self.check_access('write', raise_exception=True)
        references = self.sudo().mapped('reference')
        if references:
            references.sudo().update(values)

    @api.multi
    def _update_reference_type(self):
        self.check_access('write', raise_exception=True)
        for record in self:
            if record.reference and record.settings.save_type != record.reference.type(
            ):
                reference = record._create_reference(record.settings,
                                                     record.directory.path,
                                                     record.name,
                                                     record.content)
                record._unlink_reference()
                record.reference = "%s,%s" % (reference._name, reference.id)

    @api.multi
    def _check_reference_values(self, values):
        self.check_access('write', raise_exception=True)
        if 'content' in values:
            self._update_reference_values({'content': values['content']})
        if 'settings' in values:
            self._update_reference_type()

    @api.multi
    def _get_content(self):
        self.ensure_one()
        self.check_access('read', raise_exception=True)
        return self.reference.sudo().content() if self.reference else None

    @api.multi
    def _unlink_reference(self):
        self.check_access('unlink', raise_exception=True)
        for tuple in [
                list((k, list(map(lambda rec: rec.reference.id, v))))
                for k, v in itertools.groupby(
                    self.sorted(key=lambda rec: rec.reference._name),
                    lambda rec: rec.reference._name)
        ]:
            self.env[tuple[0]].sudo().browse(list(filter(None,
                                                         tuple[1]))).unlink()
Exemplo n.º 25
0
class KsGlobalDiscountInvoice(models.Model):
    # _inherit = "account.invoice"
    """ changing the model to account.move """
    _inherit = "account.move"

    ks_global_discount_type = fields.Selection(
        [('percent', 'Percentage'), ('amount', 'Amount')],
        string='Universal Discount Type',
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'sent': [('readonly', False)]
        },
        default='percent')
    ks_global_discount_rate = fields.Float('Universal Discount',
                                           readonly=True,
                                           states={
                                               'draft': [('readonly', False)],
                                               'sent': [('readonly', False)]
                                           })
    ks_amount_discount = fields.Monetary(string='Universal Discount',
                                         readonly=True,
                                         compute='_compute_amount',
                                         store=True,
                                         track_visibility='always')
    ks_enable_discount = fields.Boolean(compute='ks_verify_discount')
    ks_sales_discount_account = fields.Text(compute='ks_verify_discount')
    ks_purchase_discount_account = fields.Text(compute='ks_verify_discount')
    inv_num = fields.Char("Invoice#", default="010101")
    cheque_no = fields.Char(string="Cheque No")
    chq_date = fields.Date(string="Cheque Date")
    purpose = fields.Char(string="Purpose")
    is_petty = fields.Boolean(string="Is petty")

    @api.constrains('name', 'journal_id', 'state')
    def _check_unique_sequence_number(self):
        moves = self.filtered(lambda move: move.state == 'posted')
        if not moves:
            return

        self.flush()

        # /!\ Computed stored fields are not yet inside the database.
        self._cr.execute(
            '''
            SELECT move2.id
            FROM account_move move
            INNER JOIN account_move move2 ON
                move2.name = move.name
                AND move2.journal_id = move.journal_id
                AND move2.type = move.type
                AND move2.id != move.id
            WHERE move.id IN %s AND move2.state = 'posted'
        ''', [tuple(moves.ids)])
        res = self._cr.fetchone()

    @api.model
    def _get_default_journal(self):
        ''' Get the default journal.
        It could either be passed through the context using the 'default_journal_id' key containing its id,
        either be determined by the default type.
        '''
        move_type = self._context.get('default_type', 'entry')
        journal_type = 'general'
        is_pty = self._context.get('default_is_petty', 0)
        if move_type in self.get_sale_types(include_receipts=True):
            journal_type = 'sale'
        elif move_type in self.get_purchase_types(include_receipts=True):
            journal_type = 'purchase'

        if self._context.get('default_journal_id'):
            journal = self.env['account.journal'].browse(
                self._context['default_journal_id'])

            if move_type != 'entry' and journal.type != journal_type:
                raise UserError(
                    _("Cannot create an invoice of type %s with a journal having %s as type."
                      ) % (move_type, journal_type))
        elif is_pty == 1:
            company_id = self._context.get('default_company_id',
                                           self.env.company.id)
            domain = [('company_id', '=', company_id),
                      ('type', '=', journal_type), ('name', '=', 'Patty Cash')]
            journal = self.env['account.journal'].search(domain)
            if not journal:
                journal = self.env['account.journal'].create({
                    'company_id': company_id,
                    'type': journal_type,
                    'name': 'Patty Cash',
                    'code': 'ptch'
                })

        else:
            company_id = self._context.get('default_company_id',
                                           self.env.company.id)
            domain = [('company_id', '=', company_id),
                      ('type', '=', journal_type)]

            journal = None
            if self._context.get('default_currency_id'):
                currency_domain = domain + [
                    ('currency_id', '=', self._context['default_currency_id'])
                ]
                journal = self.env['account.journal'].search(currency_domain,
                                                             limit=1)

            if not journal:
                journal = self.env['account.journal'].search(domain, limit=1)

            if not journal:
                error_msg = _(
                    'Please define an accounting miscellaneous journal in your company'
                )
                if journal_type == 'sale':
                    error_msg = _(
                        'Please define an accounting sale journal in your company'
                    )
                elif journal_type == 'purchase':
                    error_msg = _(
                        'Please define an accounting purchase journal in your company'
                    )
                raise UserError(error_msg)
        return journal

    # @api.multi
    @api.depends('name')
    def ks_verify_discount(self):
        for rec in self:
            rec.ks_enable_discount = rec.env['ir.config_parameter'].sudo(
            ).get_param('ks_enable_discount')
            rec.ks_sales_discount_account = rec.env[
                'ir.config_parameter'].sudo().get_param(
                    'ks_sales_discount_account')
            rec.ks_purchase_discount_account = rec.env[
                'ir.config_parameter'].sudo().get_param(
                    'ks_purchase_discount_account')

    # @api.multi
    # 1. tax_line_ids is replaced with tax_line_id. 2. api.mulit is also removed.
    # @api.depends('invoice_line_ids.price_subtotal', 'tax_line_ids.amount', 'tax_line_ids.amount_rounding',
    #              'currency_id', 'company_id', 'date_invoice', 'type', 'ks_global_discount_type',
    #              'ks_global_discount_rate')
    @api.depends('line_ids.debit', 'line_ids.credit', 'line_ids.currency_id',
                 'line_ids.amount_currency', 'line_ids.amount_residual',
                 'line_ids.amount_residual_currency',
                 'line_ids.payment_id.state', 'ks_global_discount_type',
                 'ks_global_discount_rate')
    def _compute_amount(self):
        for rec in self:
            res = super(KsGlobalDiscountInvoice, rec)._compute_amount()
            if not ('ks_global_tax_rate' in rec):
                rec.ks_calculate_discount()
            sign = rec.type in ['in_refund', 'out_refund'] and -1 or 1
            rec.amount_total_company_signed = rec.amount_total * sign
            rec.amount_total_signed = rec.amount_total * sign
        return res

    # @api.multi
    def ks_calculate_discount(self):
        for rec in self:
            if rec.ks_global_discount_type == "amount":
                rec.ks_amount_discount = rec.ks_global_discount_rate if rec.amount_untaxed > 0 else 0
            elif rec.ks_global_discount_type == "percent":
                if rec.ks_global_discount_rate != 0.0:
                    rec.ks_amount_discount = (
                        rec.amount_untaxed +
                        rec.amount_tax) * rec.ks_global_discount_rate / 100
                else:
                    rec.ks_amount_discount = 0
            elif not rec.ks_global_discount_type:
                rec.ks_global_discount_rate = 0
                rec.ks_amount_discount = 0
            rec.amount_total = rec.amount_tax + rec.amount_untaxed - rec.ks_amount_discount
            rec.ks_update_universal_discount()

    @api.constrains('ks_global_discount_rate')
    def ks_check_discount_value(self):
        if self.ks_global_discount_type == "percent":
            if self.ks_global_discount_rate > 100 or self.ks_global_discount_rate < 0:
                raise ValidationError(
                    'You cannot enter percentage value greater than 100.')
        else:
            if self.ks_global_discount_rate < 0 or self.amount_untaxed < 0:
                raise ValidationError(
                    'You cannot enter discount amount greater than actual cost or value lower than 0.'
                )

    # @api.onchange('purchase_id')
    # def ks_get_purchase_order_discount(self):
    #     self.ks_global_discount_rate = self.purchase_id.ks_global_discount_rate
    #     self.ks_global_discount_type = self.purchase_id.ks_global_discount_type

    # @api.model
    # def invoice_line_move_line_get(self):
    #     ks_res = super(KsGlobalDiscountInvoice, self).invoice_line_move_line_get()
    #     if self.ks_amount_discount > 0:
    #         ks_name = "Universal Discount"
    #         if self.ks_global_discount_type == "percent":
    #             ks_name = ks_name + " (" + str(self.ks_global_discount_rate) + "%)"
    #         ks_name = ks_name + " for " + (self.origin if self.origin else ("Invoice No " + str(self.id)))
    #         if self.ks_sales_discount_account and (self.type == "out_invoice" or self.type == "out_refund"):
    #
    #             dict = {
    #                 'invl_id': self.number,
    #                 'type': 'src',
    #                 'name': ks_name,
    #                 'price_unit': self.move_id.ks_amount_discount,
    #                 'quantity': 1,
    #                 'amount': -self.move_id.ks_amount_discount,
    #                 'account_id': int(self.move_id.ks_sales_discount_account),
    #                 'move_id': self.id,
    #                 'date': self.date,
    #                 'user_id': self.move_id.invoice_user_id.id or self._uid,
    #                 'company_id': self.move_id.account_id.company_id.id or self.env.company.id,
    #             }
    #             ks_res.append(dict)
    #
    #         elif self.ks_purchase_discount_account and (self.type == "in_invoice" or self.type == "in_refund"):
    #             dict = {
    #                 'invl_id': self.number,
    #                 'type': 'src',
    #                 'name': ks_name,
    #                 'price_unit': self.ks_amount_discount,
    #                 'quantity': 1,
    #                 'price': -self.ks_amount_discount,
    #                 'account_id': int(self.ks_purchase_discount_account),
    #
    #                 'invoice_id': self.id,
    #             }
    #             ks_res.append(dict)
    #
    #     return ks_res

    @api.model
    def _prepare_refund(self,
                        invoice,
                        date_invoice=None,
                        date=None,
                        description=None,
                        journal_id=None):
        ks_res = super(KsGlobalDiscountInvoice,
                       self)._prepare_refund(invoice,
                                             date_invoice=None,
                                             date=None,
                                             description=None,
                                             journal_id=None)
        ks_res['ks_global_discount_rate'] = self.ks_global_discount_rate
        ks_res['ks_global_discount_type'] = self.ks_global_discount_type
        return ks_res

    def ks_update_universal_discount(self):
        """This Function Updates the Universal Discount through Sale Order"""
        for rec in self:
            already_exists = self.line_ids.filtered(
                lambda line: line.name and line.name.find('Universal Discount'
                                                          ) == 0)
            terms_lines = self.line_ids.filtered(
                lambda line: line.account_id.user_type_id.type in
                ('receivable', 'payable'))
            other_lines = self.line_ids.filtered(
                lambda line: line.account_id.user_type_id.type not in
                ('receivable', 'payable'))
            if already_exists:
                amount = rec.ks_amount_discount
                if rec.ks_sales_discount_account \
                        and (rec.type == "out_invoice"
                             or rec.type == "out_refund")\
                        and amount > 0:
                    if rec.type == "out_invoice":
                        already_exists.update({
                            'debit':
                            amount > 0.0 and amount or 0.0,
                            'credit':
                            amount < 0.0 and -amount or 0.0,
                        })
                    else:
                        already_exists.update({
                            'debit':
                            amount < 0.0 and -amount or 0.0,
                            'credit':
                            amount > 0.0 and amount or 0.0,
                        })
                if rec.ks_purchase_discount_account \
                        and (rec.type == "in_invoice"
                             or rec.type == "in_refund")\
                        and amount > 0:
                    if rec.type == "in_invoice":
                        already_exists.update({
                            'debit':
                            amount < 0.0 and -amount or 0.0,
                            'credit':
                            amount > 0.0 and amount or 0.0,
                        })
                    else:
                        already_exists.update({
                            'debit':
                            amount > 0.0 and amount or 0.0,
                            'credit':
                            amount < 0.0 and -amount or 0.0,
                        })
                total_balance = sum(other_lines.mapped('balance'))
                total_amount_currency = sum(
                    other_lines.mapped('amount_currency'))
                terms_lines.update({
                    'amount_currency':
                    -total_amount_currency,
                    'debit':
                    total_balance < 0.0 and -total_balance or 0.0,
                    'credit':
                    total_balance > 0.0 and total_balance or 0.0,
                })
            if not already_exists and rec.ks_global_discount_rate > 0:
                in_draft_mode = self != self._origin
                if not in_draft_mode and rec.type == 'out_invoice':
                    rec._recompute_universal_discount_lines()
                print()

    @api.onchange('ks_global_discount_rate', 'ks_global_discount_type',
                  'line_ids')
    def _recompute_universal_discount_lines(self):
        """This Function Create The General Entries for Universal Discount"""
        for rec in self:
            type_list = [
                'out_invoice', 'out_refund', 'in_invoice', 'in_refund'
            ]
            if rec.ks_global_discount_rate > 0 and rec.type in type_list:
                if rec.is_invoice(include_receipts=True):
                    in_draft_mode = self != self._origin
                    ks_name = "Universal Discount "
                    if rec.ks_global_discount_type == "amount":
                        ks_value = "of amount #" + str(
                            self.ks_global_discount_rate)
                    elif rec.ks_global_discount_type == "percent":
                        ks_value = " @" + str(
                            self.ks_global_discount_rate) + "%"
                    else:
                        ks_value = ''
                    ks_name = ks_name + ks_value
                    #           ("Invoice No: " + str(self.ids)
                    #            if self._origin.id
                    #            else (self.display_name))
                    terms_lines = self.line_ids.filtered(
                        lambda line: line.account_id.user_type_id.type in
                        ('receivable', 'payable'))
                    already_exists = self.line_ids.filtered(
                        lambda line: line.name and line.name.find(
                            'Universal Discount') == 0)
                    if already_exists:
                        amount = self.ks_amount_discount
                        if self.ks_sales_discount_account \
                                and (self.type == "out_invoice"
                                     or self.type == "out_refund"):
                            if self.type == "out_invoice":
                                already_exists.update({
                                    'name':
                                    ks_name,
                                    'debit':
                                    amount > 0.0 and amount or 0.0,
                                    'credit':
                                    amount < 0.0 and -amount or 0.0,
                                })
                            else:
                                already_exists.update({
                                    'name':
                                    ks_name,
                                    'debit':
                                    amount < 0.0 and -amount or 0.0,
                                    'credit':
                                    amount > 0.0 and amount or 0.0,
                                })
                        if self.ks_purchase_discount_account\
                                and (self.type == "in_invoice"
                                     or self.type == "in_refund"):
                            if self.type == "in_invoice":
                                already_exists.update({
                                    'name':
                                    ks_name,
                                    'debit':
                                    amount < 0.0 and -amount or 0.0,
                                    'credit':
                                    amount > 0.0 and amount or 0.0,
                                })
                            else:
                                already_exists.update({
                                    'name':
                                    ks_name,
                                    'debit':
                                    amount > 0.0 and amount or 0.0,
                                    'credit':
                                    amount < 0.0 and -amount or 0.0,
                                })
                    else:
                        new_tax_line = self.env['account.move.line']
                        create_method = in_draft_mode and \
                                        self.env['account.move.line'].new or\
                                        self.env['account.move.line'].create

                        if self.ks_sales_discount_account \
                                and (self.type == "out_invoice"
                                     or self.type == "out_refund"):
                            amount = self.ks_amount_discount
                            dict = {
                                'move_name':
                                self.name,
                                'name':
                                ks_name,
                                'price_unit':
                                self.ks_amount_discount,
                                'quantity':
                                1,
                                'debit':
                                amount < 0.0 and -amount or 0.0,
                                'credit':
                                amount > 0.0 and amount or 0.0,
                                'account_id':
                                int(self.ks_sales_discount_account),
                                'move_id':
                                self._origin,
                                'date':
                                self.date,
                                'exclude_from_invoice_tab':
                                True,
                                'partner_id':
                                terms_lines.partner_id.id,
                                'company_id':
                                terms_lines.company_id.id,
                                'company_currency_id':
                                terms_lines.company_currency_id.id,
                            }
                            if self.type == "out_invoice":
                                dict.update({
                                    'debit':
                                    amount > 0.0 and amount or 0.0,
                                    'credit':
                                    amount < 0.0 and -amount or 0.0,
                                })
                            else:
                                dict.update({
                                    'debit':
                                    amount < 0.0 and -amount or 0.0,
                                    'credit':
                                    amount > 0.0 and amount or 0.0,
                                })
                            if in_draft_mode:
                                self.line_ids += create_method(dict)
                                # Updation of Invoice Line Id
                                duplicate_id = self.invoice_line_ids.filtered(
                                    lambda line: line.name and line.name.find(
                                        'Universal Discount') == 0)
                                self.invoice_line_ids = self.invoice_line_ids - duplicate_id
                            else:
                                dict.update({
                                    'price_unit': 0.0,
                                    'debit': 0.0,
                                    'credit': 0.0,
                                })
                                self.line_ids = [(0, 0, dict)]

                        if self.ks_purchase_discount_account\
                                and (self.type == "in_invoice"
                                     or self.type == "in_refund"):
                            amount = self.ks_amount_discount
                            dict = {
                                'move_name':
                                self.name,
                                'name':
                                ks_name,
                                'price_unit':
                                self.ks_amount_discount,
                                'quantity':
                                1,
                                'debit':
                                amount > 0.0 and amount or 0.0,
                                'credit':
                                amount < 0.0 and -amount or 0.0,
                                'account_id':
                                int(self.ks_purchase_discount_account),
                                'move_id':
                                self.id,
                                'date':
                                self.date,
                                'exclude_from_invoice_tab':
                                True,
                                'partner_id':
                                terms_lines.partner_id.id,
                                'company_id':
                                terms_lines.company_id.id,
                                'company_currency_id':
                                terms_lines.company_currency_id.id,
                            }

                            if self.type == "in_invoice":
                                dict.update({
                                    'debit':
                                    amount < 0.0 and -amount or 0.0,
                                    'credit':
                                    amount > 0.0 and amount or 0.0,
                                })
                            else:
                                dict.update({
                                    'debit':
                                    amount > 0.0 and amount or 0.0,
                                    'credit':
                                    amount < 0.0 and -amount or 0.0,
                                })
                            self.line_ids += create_method(dict)
                            # updation of invoice line id
                            duplicate_id = self.invoice_line_ids.filtered(
                                lambda line: line.name and line.name.find(
                                    'Universal Discount') == 0)
                            self.invoice_line_ids = self.invoice_line_ids - duplicate_id

                    if in_draft_mode:
                        # Update the payement account amount
                        terms_lines = self.line_ids.filtered(
                            lambda line: line.account_id.user_type_id.type in
                            ('receivable', 'payable'))
                        other_lines = self.line_ids.filtered(
                            lambda line: line.account_id.user_type_id.type
                            not in ('receivable', 'payable'))
                        total_balance = sum(other_lines.mapped('balance'))
                        total_amount_currency = sum(
                            other_lines.mapped('amount_currency'))
                        terms_lines.update({
                            'amount_currency':
                            -total_amount_currency,
                            'debit':
                            total_balance < 0.0 and -total_balance or 0.0,
                            'credit':
                            total_balance > 0.0 and total_balance or 0.0,
                        })
                    else:
                        terms_lines = self.line_ids.filtered(
                            lambda line: line.account_id.user_type_id.type in
                            ('receivable', 'payable'))
                        other_lines = self.line_ids.filtered(
                            lambda line: line.account_id.user_type_id.type
                            not in ('receivable', 'payable'))
                        already_exists = self.line_ids.filtered(
                            lambda line: line.name and line.name.find(
                                'Universal Discount') == 0)
                        total_balance = sum(
                            other_lines.mapped('balance')) + amount
                        total_amount_currency = sum(
                            other_lines.mapped('amount_currency'))
                        dict1 = {
                            'debit': amount > 0.0 and amount or 0.0,
                            'credit': amount < 0.0 and -amount or 0.0,
                        }
                        dict2 = {
                            'debit': total_balance < 0.0 and -total_balance
                            or 0.0,
                            'credit': total_balance > 0.0 and total_balance
                            or 0.0,
                        }
                        self.line_ids = [(1, already_exists.id, dict1),
                                         (1, terms_lines.id, dict2)]
                        print()

            elif self.ks_global_discount_rate <= 0:
                already_exists = self.line_ids.filtered(
                    lambda line: line.name and line.name.find(
                        'Universal Discount') == 0)
                if already_exists:
                    self.line_ids -= already_exists
                    terms_lines = self.line_ids.filtered(
                        lambda line: line.account_id.user_type_id.type in
                        ('receivable', 'payable'))
                    other_lines = self.line_ids.filtered(
                        lambda line: line.account_id.user_type_id.type not in
                        ('receivable', 'payable'))
                    total_balance = sum(other_lines.mapped('balance'))
                    total_amount_currency = sum(
                        other_lines.mapped('amount_currency'))
                    terms_lines.update({
                        'amount_currency':
                        -total_amount_currency,
                        'debit':
                        total_balance < 0.0 and -total_balance or 0.0,
                        'credit':
                        total_balance > 0.0 and total_balance or 0.0,
                    })

    def print_credit_report(self):
        return self.env.ref('cost_sheet_quotations.action_credit_note_print'
                            ).report_action(self)
Exemplo n.º 26
0
class AccountAssetAsset(models.Model):
    _name = 'account.asset.asset'
    _description = 'Asset/Revenue Recognition'
    _inherit = ['mail.thread', 'ir.needaction_mixin']

    entry_count = fields.Integer(compute='_entry_count', string='# Asset Entries')
    name = fields.Char(string='Asset Name', required=True, readonly=True, states={'draft': [('readonly', False)]})
    code = fields.Char(string='Reference', size=32, readonly=True, states={'draft': [('readonly', False)]})
    value = fields.Float(string='Gross Value', required=True, readonly=True, digits=0, states={'draft': [('readonly', False)]}, oldname='purchase_value')
    currency_id = fields.Many2one('res.currency', string='Currency', required=True, readonly=True, states={'draft': [('readonly', False)]},
        default=lambda self: self.env.user.company_id.currency_id.id)
    company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, states={'draft': [('readonly', False)]},
        default=lambda self: self.env['res.company']._company_default_get('account.asset.asset'))
    note = fields.Text()
    category_id = fields.Many2one('account.asset.category', string='Category', required=True, change_default=True, readonly=True, states={'draft': [('readonly', False)]})
    date = fields.Date(string='Date', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=fields.Date.context_today, oldname="purchase_date")
    state = fields.Selection([('draft', 'Draft'), ('open', 'Running'), ('close', 'Close')], 'Status', required=True, copy=False, default='draft',
        help="When an asset is created, the status is 'Draft'.\n"
            "If the asset is confirmed, the status goes in 'Running' and the depreciation lines can be posted in the accounting.\n"
            "You can manually close an asset when the depreciation is over. If the last line of depreciation is posted, the asset automatically goes in that status.")
    active = fields.Boolean(default=True)
    partner_id = fields.Many2one('res.partner', string='Partner', readonly=True, states={'draft': [('readonly', False)]})
    method = fields.Selection([('linear', 'Linear'), ('degressive', 'Degressive')], string='Computation Method', required=True, readonly=True, states={'draft': [('readonly', False)]}, 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', readonly=True, states={'draft': [('readonly', False)]}, default=5, help="The number of depreciations needed to depreciate your asset")
    method_period = fields.Integer(string='Number of Months in a Period', required=True, readonly=True, default=12, states={'draft': [('readonly', False)]},
        help="The amount of time between two depreciations, in months")
    method_end = fields.Date(string='Ending Date', readonly=True, states={'draft': [('readonly', False)]})
    method_progress_factor = fields.Float(string='Degressive Factor', readonly=True, default=0.3, states={'draft': [('readonly', False)]})
    value_residual = fields.Float(compute='_amount_residual', method=True, digits=0, string='Residual Value')
    method_time = fields.Selection([('number', 'Number of Depreciations'), ('end', 'Ending Date')], string='Time Method', required=True, readonly=True, default='number', states={'draft': [('readonly', False)]},
        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.")
    prorata = fields.Boolean(string='Prorata Temporis', readonly=True, states={'draft': [('readonly', False)]},
        help='Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first January / Start date of fiscal year')
    depreciation_line_ids = fields.One2many('account.asset.depreciation.line', 'asset_id', string='Depreciation Lines', readonly=True, states={'draft': [('readonly', False)], 'open': [('readonly', False)]})
    salvage_value = fields.Float(string='Salvage Value', digits=0, readonly=True, states={'draft': [('readonly', False)]},
        help="It is the amount you plan to have that you cannot depreciate.")
    invoice_id = fields.Many2one('account.invoice', string='Invoice', states={'draft': [('readonly', False)]}, copy=False)
    type = fields.Selection(related="category_id.type", string='Type', required=True)

    @api.multi
    def unlink(self):
        for asset in self:
            if asset.state in ['open', 'close']:
                raise UserError(_('You cannot delete a document is in %s state.') % (asset.state,))
            for depreciation_line in asset.depreciation_line_ids:
                if depreciation_line.move_id:
                    raise UserError(_('You cannot delete a document that contains posted entries.'))
        return super(AccountAssetAsset, self).unlink()

    @api.multi
    def _get_last_depreciation_date(self):
        """
        @param id: ids of a account.asset.asset objects
        @return: Returns a dictionary of the effective dates of the last depreciation entry made for given asset ids. If there isn't any, return the purchase date of this asset
        """
        self.env.cr.execute("""
            SELECT a.id as id, COALESCE(MAX(m.date),a.date) AS date
            FROM account_asset_asset a
            LEFT JOIN account_asset_depreciation_line rel ON (rel.asset_id = a.id)
            LEFT JOIN account_move m ON (rel.move_id = m.id)
            WHERE a.id IN %s
            GROUP BY a.id, m.date """, (tuple(self.ids),))
        result = dict(self.env.cr.fetchall())
        return result

    @api.model
    def _cron_generate_entries(self):
        self.compute_generated_entries(datetime.today())

    @api.model
    def compute_generated_entries(self, date, asset_type=None):
        # Entries generated : one by grouped category and one by asset from ungrouped category
        created_move_ids = []
        type_domain = []
        if asset_type:
            type_domain = [('type', '=', asset_type)]

        ungrouped_assets = self.env['account.asset.asset'].search(type_domain + [('state', '=', 'open'), ('category_id.group_entries', '=', False)])
        created_move_ids += ungrouped_assets._compute_entries(date, group_entries=False)

        for grouped_category in self.env['account.asset.category'].search(type_domain + [('group_entries', '=', True)]):
            assets = self.env['account.asset.asset'].search([('state', '=', 'open'), ('category_id', '=', grouped_category.id)])
            created_move_ids += assets._compute_entries(date, group_entries=True)
        return created_move_ids

    def _compute_board_amount(self, sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date):
        amount = 0
        if sequence == undone_dotation_number:
            amount = residual_amount
        else:
            if self.method == 'linear':
                amount = amount_to_depr / (undone_dotation_number - len(posted_depreciation_line_ids))
                if self.prorata and self.category_id.type == 'purchase':
                    amount = amount_to_depr / self.method_number
                    if sequence == 1:
                        days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
                        amount = (amount_to_depr / self.method_number) / total_days * days
            elif self.method == 'degressive':
                amount = residual_amount * self.method_progress_factor
                if self.prorata:
                    if sequence == 1:
                        days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1
                        amount = (residual_amount * self.method_progress_factor) / total_days * days
        return amount

    def _compute_board_undone_dotation_nb(self, depreciation_date, total_days):
        undone_dotation_number = self.method_number
        if self.method_time == 'end':
            end_date = datetime.strptime(self.method_end, DF).date()
            undone_dotation_number = 0
            while depreciation_date <= end_date:
                depreciation_date = date(depreciation_date.year, depreciation_date.month, depreciation_date.day) + relativedelta(months=+self.method_period)
                undone_dotation_number += 1
        if self.prorata and self.category_id.type == 'purchase':
            undone_dotation_number += 1
        return undone_dotation_number

    @api.multi
    def compute_depreciation_board(self):
        self.ensure_one()

        posted_depreciation_line_ids = self.depreciation_line_ids.filtered(lambda x: x.move_check).sorted(key=lambda l: l.depreciation_date)
        unposted_depreciation_line_ids = self.depreciation_line_ids.filtered(lambda x: not x.move_check)

        # Remove old unposted depreciation lines. We cannot use unlink() with One2many field
        commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids]

        if self.value_residual != 0.0:
            amount_to_depr = residual_amount = self.value_residual
            if self.prorata:
                depreciation_date = datetime.strptime(self._get_last_depreciation_date()[self.id], DF).date()
            else:
                # depreciation_date = 1st of January of purchase year if annual valuation, 1st of
                # purchase month in other cases
                if self.method_period >= 12:
                    asset_date = datetime.strptime(self.date[:4] + '-01-01', DF).date()
                else:
                    asset_date = datetime.strptime(self.date[:7] + '-01', DF).date()
                # if we already have some previous validated entries, starting date isn't 1st January but last entry + method period
                if posted_depreciation_line_ids and posted_depreciation_line_ids[-1].depreciation_date:
                    last_depreciation_date = datetime.strptime(posted_depreciation_line_ids[-1].depreciation_date, DF).date()
                    depreciation_date = last_depreciation_date + relativedelta(months=+self.method_period)
                else:
                    depreciation_date = asset_date
            day = depreciation_date.day
            month = depreciation_date.month
            year = depreciation_date.year
            total_days = (year % 4) and 365 or 366

            undone_dotation_number = self._compute_board_undone_dotation_nb(depreciation_date, total_days)

            for x in range(len(posted_depreciation_line_ids), undone_dotation_number):
                sequence = x + 1
                amount = self._compute_board_amount(sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date)
                amount = self.currency_id.round(amount)
                residual_amount -= amount
                vals = {
                    'amount': amount,
                    'asset_id': self.id,
                    'sequence': sequence,
                    'name': (self.code or '') + '/' + str(sequence),
                    'remaining_value': residual_amount,
                    'depreciated_value': self.value - (self.salvage_value + residual_amount),
                    'depreciation_date': depreciation_date.strftime(DF),
                }
                commands.append((0, False, vals))
                # Considering Depr. Period as months
                depreciation_date = date(year, month, day) + relativedelta(months=+self.method_period)
                day = depreciation_date.day
                month = depreciation_date.month
                year = depreciation_date.year

        self.write({'depreciation_line_ids': commands})

        return True

    @api.multi
    def validate(self):
        self.write({'state': 'open'})
        fields = [
            'method',
            'method_number',
            'method_period',
            'method_end',
            'method_progress_factor',
            'method_time',
            'salvage_value',
            'invoice_id',
        ]
        ref_tracked_fields = self.env['account.asset.asset'].fields_get(fields)
        for asset in self:
            tracked_fields = ref_tracked_fields.copy()
            if asset.method == 'linear':
                del(tracked_fields['method_progress_factor'])
            if asset.method_time != 'end':
                del(tracked_fields['method_end'])
            else:
                del(tracked_fields['method_number'])
            dummy, tracking_value_ids = asset._message_track(tracked_fields, dict.fromkeys(fields))
            asset.message_post(subject=_('Asset created'), tracking_value_ids=tracking_value_ids)

    @api.multi
    def set_to_close(self):
        move_ids = []
        for asset in self:
            unposted_depreciation_line_ids = asset.depreciation_line_ids.filtered(lambda x: not x.move_check)
            if unposted_depreciation_line_ids:
                old_values = {
                    'method_end': asset.method_end,
                    'method_number': asset.method_number,
                }

                # Remove all unposted depr. lines
                commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids]

                # Create a new depr. line with the residual amount and post it
                sequence = len(asset.depreciation_line_ids) - len(unposted_depreciation_line_ids) + 1
                today = datetime.today().strftime(DF)
                vals = {
                    'amount': asset.value_residual,
                    'asset_id': asset.id,
                    'sequence': sequence,
                    'name': (asset.code or '') + '/' + str(sequence),
                    'remaining_value': 0,
                    'depreciated_value': asset.value - asset.salvage_value,  # the asset is completely depreciated
                    'depreciation_date': today,
                }
                commands.append((0, False, vals))
                asset.write({'depreciation_line_ids': commands, 'method_end': today, 'method_number': sequence})
                tracked_fields = self.env['account.asset.asset'].fields_get(['method_number', 'method_end'])
                changes, tracking_value_ids = asset._message_track(tracked_fields, old_values)
                if changes:
                    asset.message_post(subject=_('Asset sold or disposed. Accounting entry awaiting for validation.'), tracking_value_ids=tracking_value_ids)
                move_ids += asset.depreciation_line_ids[-1].create_move(post_move=False)
        if move_ids:
            name = _('Disposal Move')
            view_mode = 'form'
            if len(move_ids) > 1:
                name = _('Disposal Moves')
                view_mode = 'tree,form'
            return {
                'name': name,
                'view_type': 'form',
                'view_mode': view_mode,
                'res_model': 'account.move',
                'type': 'ir.actions.act_window',
                'target': 'current',
                'res_id': move_ids[0],
            }

    @api.multi
    def set_to_draft(self):
        self.write({'state': 'draft'})

    @api.one
    @api.depends('value', 'salvage_value', 'depreciation_line_ids.move_check', 'depreciation_line_ids.amount')
    def _amount_residual(self):
        total_amount = 0.0
        for line in self.depreciation_line_ids:
            if line.move_check:
                total_amount += line.amount
        self.value_residual = self.value - total_amount - self.salvage_value

    @api.onchange('company_id')
    def onchange_company_id(self):
        self.currency_id = self.company_id.currency_id.id

    @api.multi
    @api.depends('depreciation_line_ids.move_id')
    def _entry_count(self):
        for asset in self:
            res = self.env['account.asset.depreciation.line'].search_count([('asset_id', '=', asset.id), ('move_id', '!=', False)])
            asset.entry_count = res or 0

    @api.one
    @api.constrains('prorata', 'method_time')
    def _check_prorata(self):
        if self.prorata and self.method_time != 'number':
            raise ValidationError(_('Prorata temporis can be applied only for time method "number of depreciations".'))

    @api.onchange('category_id')
    def onchange_category_id(self):
        vals = self.onchange_category_id_values(self.category_id.id)
        # We cannot use 'write' on an object that doesn't exist yet
        if vals:
            for k, v in vals['value'].iteritems():
                setattr(self, k, v)

    def onchange_category_id_values(self, category_id):
        if category_id:
            category = self.env['account.asset.category'].browse(category_id)
            return {
                'value': {
                    'method': category.method,
                    'method_number': category.method_number,
                    'method_time': category.method_time,
                    'method_period': category.method_period,
                    'method_progress_factor': category.method_progress_factor,
                    'method_end': category.method_end,
                    'prorata': category.prorata,
                }
            }

    @api.onchange('method_time')
    def onchange_method_time(self):
        if self.method_time != 'number':
            self.prorata = False

    @api.multi
    def copy_data(self, default=None):
        if default is None:
            default = {}
        default['name'] = self.name + _(' (copy)')
        return super(AccountAssetAsset, self).copy_data(default)

    @api.multi
    def _compute_entries(self, date, group_entries=False):
        depreciation_ids = self.env['account.asset.depreciation.line'].with_context(depreciation_date=date).search([
            ('asset_id', 'in', self.ids), ('depreciation_date', '<=', date),
            ('move_check', '=', False)])
        if group_entries:
            return depreciation_ids.create_grouped_move()
        return depreciation_ids.create_move()

    @api.model
    def create(self, vals):
        asset = super(AccountAssetAsset, self.with_context(mail_create_nolog=True)).create(vals)
        asset.compute_depreciation_board()
        return asset

    @api.multi
    def write(self, vals):
        res = super(AccountAssetAsset, self).write(vals)
        if 'depreciation_line_ids' not in vals and 'state' not in vals:
            for rec in self:
                rec.compute_depreciation_board()
        return res

    @api.multi
    def open_entries(self):
        move_ids = []
        for asset in self:
            for depreciation_line in asset.depreciation_line_ids:
                if depreciation_line.move_id:
                    move_ids.append(depreciation_line.move_id.id)
        return {
            'name': _('Journal Entries'),
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'account.move',
            'view_id': False,
            'type': 'ir.actions.act_window',
            'domain': [('id', 'in', move_ids)]
        }
Exemplo n.º 27
0
class SaleOrder(models.Model):
    _inherit = "sale.order"
    _description = "Sales Order"

    purchase_order_id = fields.Many2one(comodel_name="purchase.order", string="PO#", copy=False)
    vendor_id = fields.Many2one(comodel_name='res.partner', string="Vendor")

    # Instructions
    colour_instructions = fields.Text(string="Colour Instructions")
    packing = fields.Text(string="Packing")
    face_stamp = fields.Html(string="Face Stamp on Paper and Booklet File")
    selvedge = fields.Html(string="Selvedge")
    shipping_mark = fields.Html(string="Shipping Mark")
    shipping_sample_book = fields.Text(string="Shippment Sample")
    notes = fields.Text(string="Notes")

    # Other details
    shipment_date = fields.Date(string="Shipment Date")
    insurance_id = fields.Many2one(comodel_name='res.insurance', string="Insurance")
    destination_id = fields.Many2one(comodel_name='res.destination', string='Destination')
    marks = fields.Char(string="Marks")

    attachment_ids = fields.One2many('ir.attachment', 'sale_id', string='Attachment')
    attachment_count = fields.Integer(compute='_compute_attachment_count')
    actual_grand_total = fields.Float(string="Actual Grand Total", compute='_compute_grand_total')

    @api.depends('order_line')
    def _compute_grand_total(self):
        grand_total = 0
        for record in self:
            for line in self.order_line:
                grand_total = grand_total + line.actual_net_amount
            record.actual_grand_total = grand_total

    @api.onchange('colour_instructions')
    def _onchange_colour_instructions(self):
        if self.purchase_order_id:
            self.purchase_order_id.colour_instructions = self.colour_instructions

    @api.onchange('packing')
    def _onchange_packing(self):
        if self.purchase_order_id:
            self.purchase_order_id.packing = self.packing

    @api.onchange('face_stamp')
    def _onchange_face_stamp(self):
        if self.purchase_order_id:
            self.purchase_order_id.face_stamp = self.face_stamp

    @api.onchange('selvedge')
    def _onchange_selvedge(self):
        if self.purchase_order_id:
            self.purchase_order_id.selvedge = self.selvedge

    @api.onchange('shipping_mark')
    def _onchange_shipping_mark(self):
        if self.purchase_order_id:
            self.purchase_order_id.shipping_mark = self.shipping_mark

    @api.onchange('shipping_sample_book')
    def _onchange_shipping_sample_book(self):
        if self.purchase_order_id:
            self.purchase_order_id.shipping_sample_book = self.shipping_sample_book

    @api.onchange('notes')
    def _onchange_notes(self):
        if self.purchase_order_id:
            self.purchase_order_id.notes = self.notes

    @api.onchange('shipment_date')
    def _onchange_shipment_date(self):
        if self.purchase_order_id:
            self.purchase_order_id.shipment_date = self.shipment_date

    @api.onchange('destination_id')
    def _onchange_destination_id(self):
        if self.purchase_order_id and self.destination_id:
            self.purchase_order_id.destination_id = self.destination_id.id

    @api.onchange('marks')
    def _onchange_marks(self):
        if self.purchase_order_id:
            self.purchase_order_id.marks = self.marks

    @api.onchange('attachment_ids')
    def _onchange_attachment_ids(self):
        if self.purchase_order_id:
            for attachment in self.attachment_ids:
                attachment.purchase_id = self.purchase_order_id.id

    def photos(self):
        return {
            'name': 'Photos',
            'view_type': 'form',
            'view_mode': 'kanban,tree,form',
            'res_model': 'ir.attachment',
            'view_id': False,
            'type': 'ir.actions.act_window',
            'context': {'default_sale_id': self.id,
                        'default_purchase_id': self.purchase_order_id.id if self.purchase_order_id else ''},
            'domain': [('sale_id', '=', self.id)]

        }

    @api.depends('attachment_ids')
    def _compute_attachment_count(self):
        for order in self:
            order.attachment_count = len(order.attachment_ids)

    def action_confirm(self):
        """ inherited to create sale order,
         first check for an existing sale order for the corresponding SO
         if does not exist, create a new purchase order"""
        for record in self:
            res = super(SaleOrder, self).action_confirm()
            if not record.purchase_order_id and record.vendor_id:
                purchase_order_lines_obj = self.env['purchase.order.line']
                attachment_ids = []
                purchase_order_obj = self.env['purchase.order']
                for attchment in record.attachment_ids:
                    attachment_ids.append((0, 0, {
                        'name': attchment.name,
                        'datas': attchment.datas,
                        "description": attchment.description,
                        "mimetype": attchment.mimetype,
                        'index_content': attchment.index_content,
                        "create_uid": attchment.create_uid.id,
                    }))
                vals = {
                    "partner_id": record.vendor_id.id,
                    "sale_order_id": record.id,
                    "customer_id": record.partner_id.id,
                    "attachment_ids": attachment_ids,
                    "colour_instructions": record.colour_instructions,
                    "packing": record.packing,
                    "face_stamp": record.face_stamp,
                    "name": record.name,
                    "selvedge": record.selvedge,
                    "shipping_mark": record.shipping_mark,
                    "shipping_sample_book": record.shipping_sample_book,
                    "notes": record.notes,
                    "marks": record.marks,
                    "shipment_date": record.shipment_date,
                    "destination_id": record.destination_id.id,
                    "currency_id": record.currency_id.id,

                }
                purchase = purchase_order_obj.create(vals)
                record.purchase_order_id = purchase.id
                for line in record.order_line:
                    taxes = line.product_id.supplier_taxes_id
                    fpos = record.fiscal_position_id
                    taxes_id = fpos.map_tax(taxes, line.product_id, record.vendor_id) if fpos else taxes
                    if taxes_id:
                        taxes_id = taxes_id.filtered(lambda x: x.company_id.id == record.company_id.id)

                    purchase_order_line = purchase_order_lines_obj.create({'product_id': line.product_id.id,
                                                                           'name': line.name,
                                                                           'product_qty': line.product_uom_qty,
                                                                           "date_planned": datetime.today(),
                                                                           "product_uom": line.product_uom.id,
                                                                           'price_unit': line.price_unit,
                                                                           "order_id": purchase.id,
                                                                           "actual_qty": line.actual_qty,
                                                                           "sale_order_line_id": line.id,
                                                                           # "discount": line.discount,
                                                                           'taxes_id': [(6, 0, taxes_id.ids)],
                                                                           })
                    line.purchase_order_line_id = purchase_order_line.id
            return res

    @api.model
    def create(self, values):
        """
        adding values to the name from sequence
        :param values:
        :return: new record id
        """

        if values.get('name', _('New')) == _('New'):
            # values['name'] = self.env['ir.sequence'].next_by_code('sale.delivery')
            values['name'] = self.env['ir.sequence'].next_by_code('order.reference',
                                                                  None) or _('New')
            # values['marks'] = values['name']
        customer_code = ''
        if values.get('partner_id'):
            customer = self.env['res.partner'].browse(values.get('partner_id'))
            customer_code = customer.customer_code
        if values.get('marks'):
            marks_field = values.get('marks')
        else:
            marks_field = ' '

        values['marks'] = '%s %s %s' % (customer_code, values['name'], marks_field)

        return super(SaleOrder, self).create(values)

    def name_get(self):
        """adding SO to the name"""
        result = []
        for r in self:
            result.append((r.id, u"%s %s" % ('SO', r.name)))
        return result

    def _prepare_invoice(self):
        res = super(SaleOrder, self)._prepare_invoice()
        res['reference'] = self.name
        res['sale_id'] = self.id
        res['ref'] = self.name
        res['is_order_to_invoice'] = True
        return res

    def create_invoices(self):
        self._create_invoices(self)
        return self.action_view_invoice()

    def _create_invoices(self, grouped=False, final=False):
        res = super(SaleOrder, self)._create_invoices()
        return res
Exemplo n.º 28
0
class DeliveryCarrier(models.Model):
    _inherit = "delivery.carrier"

    tracking_url = fields.Text(string="Tracking URL")
Exemplo n.º 29
0
class MedicalDocumentReference(models.Model):
    # FHIR Entity: Document Reference
    # (https://www.hl7.org/fhir/documentreference.html)
    _name = "medical.document.reference"
    _description = "Medical Document Reference"
    _inherit = ["medical.request", "medical.document.language"]

    internal_identifier = fields.Char(string="Document reference")
    state = fields.Selection(
        [
            ("draft", "Draft"),
            ("current", "Current"),
            ("superseded", "Superseded"),
        ],
        required=True,
        track_visibility=True,
        default="draft",
    )
    document_type_id = fields.Many2one(
        "medical.document.type",
        required=True,
        readonly=True,
        states={"draft": [("readonly", False)]},
        ondelete="restrict",
    )
    document_type = fields.Selection(related="document_type_id.document_type",
                                     readonly=True)
    document_template_id = fields.Many2one(
        "medical.document.template",
        readonly=True,
        copy=False,
        ondelete="restrict",
    )
    is_editable = fields.Boolean(compute="_compute_is_editable")
    text = fields.Text(string="Document text",
                       readonly=True,
                       copy=False,
                       sanitize=True)
    lang = fields.Selection(
        required=False,
        readonly=True,
        copy=False,
        states={"draft": [("readonly", False)]},
    )

    def _get_language(self):
        return self.lang or self.patient_id.lang

    def check_is_billable(self):
        return self.is_billable

    def _get_internal_identifier(self, vals):
        return (
            self.env["ir.sequence"].next_by_code("medical.document.reference")
            or "/")

    @api.multi
    @api.depends("state")
    def _compute_is_editable(self):
        for rec in self:
            rec.is_editable = bool(rec.state == "draft")

    def action_view_request_parameters(self):
        return {
            "view": "medical_document.medical_document_reference_action",
            "view_form": "medical.document.reference.view.form",
        }

    def _get_parent_field_name(self):
        return "document_reference_id"

    @api.multi
    def print(self):
        return self._print(self.print_action)

    @api.multi
    def view(self):
        return self._print(self.view_action)

    @api.multi
    def render(self):
        return self._print(self.render_report)

    def _print(self, action):
        self.ensure_one()
        if self.state == "draft":
            return self._draft2current(action)
        return action()

    def _render(self):
        return self.with_context(
            lang=self.lang).document_type_id.report_action_id.render(self.id)

    def render_report(self):
        return base64.b64encode(self._render()[0])

    def view_action(self):
        if self.document_type == "action":
            return self.document_type_id.report_action_id.report_action(self)
        raise UserError(_("Function must be defined"))

    def _get_printer_usage(self):
        return "standard"

    def print_action(self):
        content, mime = self._render()
        behaviour = self.remote.with_context(
            printer_usage=self._get_printer_usage()).get_printer_behaviour()
        if "printer" not in behaviour:
            return False
        printer = behaviour.pop("printer")
        return printer.with_context(
            print_report_name="doc_" +
            self.internal_identifier).print_document(
                report=self.document_type_id.report_action_id,
                content=content,
                doc_format=mime,
            )

    @api.multi
    def draft2current(self):
        return self._draft2current(self.print_action)

    @api.multi
    def cancel(self):
        pass

    def draft2current_values(self):
        template_id = self.document_type_id.current_template_id.id
        return {
            "lang":
            self._get_language(),
            "document_template_id":
            template_id,
            "text":
            self.with_context(
                template_id=template_id,
                render_language=self._get_language()).render_text(),
        }

    @api.multi
    def change_lang(self, lang):
        text = self.with_context(template_id=self.document_template_id.id,
                                 render_language=lang).render_text()
        return self.write({"lang": lang, "text": text})

    def _draft2current(self, action):
        self.ensure_one()
        if self.state != "draft":
            raise ValidationError(_("State must be draft"))
        self.write(self.draft2current_values())
        res = action()
        if res:
            self.write({"state": "current"})
        return res

    def render_text(self):
        if self.document_type == "action":
            template = self.document_template_id or self.env[
                "medical.document.template"].browse(
                    self._context.get("template_id", False))
            return template.render_template(self._name, self.id)
        raise UserError(_("Function must be defined"))

    def current2superseded_values(self):
        return {"state": "superseded"}

    @api.multi
    def current2superseded(self):
        if self.filtered(lambda r: r.state != "current"):
            raise ValidationError(_("State must be Current"))
        self.write(self.current2superseded_values())
Exemplo n.º 30
0
class HospitalPatient(models.Model):
    _name = 'hospital.patient'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _description = 'Patient Record'
    _rec_name = 'patient_name'
    # rec.xml_id = res.get(rec.id)

    # name = fields.Char(string="Contact Number")
    name_seq = fields.Char(string='Patient ID',
                           required=True,
                           copy=False,
                           readonly=True,
                           index=True,
                           default=lambda self: _('New'))
    gender = fields.Selection([
        ('male', 'Male'),
        ('fe_male', 'Female'),
    ],
                              default='male',
                              string="Gender")
    age_group = fields.Selection([
        ('major', 'Major'),
        ('minor', 'Minor'),
    ],
                                 string="Age Group",
                                 compute='set_age_group',
                                 store=True)
    patient_name = fields.Char(string='Name',
                               required=True,
                               track_visibility="always")
    # add  track visibility (already done in my copy/paste !
    patient_age = fields.Integer('Age',
                                 track_visibility="always",
                                 group_operator=False)
    patient_age2 = fields.Float(string="Age2", required=False)
    notes = fields.Text(string="Registration Note")
    image = fields.Binary(string="Image", attachment=True)
    appointment_count = fields.Integer(string='Appointment',
                                       compute='get_appointment_count')
    active = fields.Boolean("Active", default=True)
    doctor_id = fields.Many2one('hospital.doctor', string="Doctor")
    email_id = fields.Char(string="Email")
    user_id = fields.Many2one('res.users', string="PRO")
    doctor_gender = fields.Selection([
        ('male', 'Male'),
        ('fe_male', 'Female'),
    ],
                                     string="Doctor Gender")
    patient_name_upper = fields.Char(compute='_compute_upper_name',
                                     inverse='_inverse_upper_name')

    # Overriding the create method to assign sequence for the record
    # https://www.youtube.com/watch?v=ZfKzmfiqeg0&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=8
    @api.model
    def create(self, vals):
        if vals.get('name_seq', _('New')) == _('New'):
            vals['name_seq'] = self.env['ir.sequence'].next_by_code(
                'hospital.patient.sequence') or _('New')
        result = super(HospitalPatient, self).create(vals)
        return result

    # compute function in Odoo
    # https://www.youtube.com/watch?v=Mg80GxrKDOc&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=11
    @api.depends('patient_name')
    def _compute_upper_name(self):
        for rec in self:
            rec.patient_name_upper = rec.patient_name.upper(
            ) if rec.patient_name else False

    # Making compute field editable using inverse function
    # https://www.youtube.com/watch?v=NEr6hUTrn84&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=47
    def _inverse_upper_name(self):
        for rec in self:
            rec.patient_name = rec.patient_name_upper.lower(
            ) if rec.patient_name_upper else False

    # Action For Smart Button
    # https://www.youtube.com/watch?v=I93Lr-bprIc&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=19
    def open_patient_appointments(self):
        return {
            'name': _('Appointments'),
            'domain': [('patient_id', '=', self.id)],
            # 'view_type': 'form',
            'res_model': 'hospital.appointment',
            'view_id': False,
            'view_mode': 'tree,form',
            'type': 'ir.actions.act_window',
        }

    def get_appointment_count(self):
        count = self.env['hospital.appointment'].search_count([('patient_id',
                                                                '=', self.id)])
        self.appointment_count = count

    @api.depends('patient_age')
    def set_age_group(self):
        for rec in self:
            if rec.patient_age:
                if rec.patient_age < 18:
                    rec.age_group = 'minor'
                else:
                    rec.age_group = 'major'

    # Add Constrains For a Field
    # https://www.youtube.com/watch?v=ijS-N1CdiWU&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=14
    @api.constrains('patient_age')
    def check_age(self):
        for rec in self:
            if rec.patient_age < 5:
                raise ValidationError(_('The Age Must be Greater Than 5..!'))

    # How to Write Onchange Functions
    # https://www.youtube.com/watch?v=qyRhjyp1MeE&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=39
    @api.onchange('doctor_id')
    def set_doctor_gender(self):
        for rec in self:
            if rec.doctor_id:
                rec.doctor_gender = rec.doctor_id.gender

    # https://www.youtube.com/watch?v=-1r3WSwtqxQ
    def name_get(self):
        # name get function for the model executes automatically
        res = []
        for rec in self:
            res.append((rec.id, '%s - %s' % (rec.name_seq, rec.patient_name)))
        return res

    # Sending Email in Button Click
    # https://www.youtube.com/watch?v=CZVRmtv6re0&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=44
    def action_send_card(self):
        # sending the patient report to patient via email
        template_id = self.env.ref(
            'jm_hospital.patient_card_email_template').id
        template = self.env['mail.template'].browse(template_id)
        template.send_mail(self.id, force_send=True)

    # Function which is executed using the Cron Job/ Scheduled Action
    # https://www.youtube.com/watch?v=_P_AVSNr6uU&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=52
    @api.model
    def test_cron_job(self):
        print("Abcd")  # print will get printed in the log of pycharm
        #code accordingly to execute the cron

    # Print PDF Report From Button Click in Form
    # https://www.youtube.com/watch?v=Dc8GDj7ygsI&list=PLqRRLx0cl0hoJhjFWkFYowveq2Zn55dhM&index=67
    def print_report(self):
        return self.env.ref('jm_hospital.report_patient_card').report_action(
            self)

    # Server action
    def action_patients(self):
        print("Odoo Mates..............")
        return {
            'name': _('Patients Server Action'),
            'domain': [],
            'view_type': 'form',
            'res_model': 'hospital.patient',
            'view_id': False,
            'view_mode': 'tree,form',
            'type': 'ir.actions.act_window',
        }